Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 5.00/16: Рейтинг темы: голосов - 16, средняя оценка - 5.00
3 / 3 / 0
Регистрация: 03.06.2019
Сообщений: 64

Как можно оптимизировать решение данной задачи?

08.01.2020, 23:35. Показов 3206. Ответов 28
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Всем привет!
Есть задачка, вот условие:

Напишите функцию, которая принимает в качестве параметра целое число n и возвращает true, если число n! % (n^2) == 0.

Вроде все понятно, вот только задача непростая, ведь максимум n, для которого она хоть что-либо может предложить(если делать по-простому, то есть считать каждое выражение), это примерно 20-25. Дальше все.

Поэтому вот алгоритм, по которому делал я:

Разбил число n ^ 2 на делители, поместил в масив nPw. Разбил числа от 1 до n на делители, поместил в values. Вычеркнул общие члены для массивов; если в nPw остались элементы, то число n! не делится на n ^ 2.
Если что, вот код:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <iostream>
#include <vector>
#include <iterator>
using namespace std;
 
//Primes() accepts certain number and returns and returns its expanded version(on primes) like vector.
 
vector<int> Primes(int n) {
    vector <int> v;
    while (n != 1) {
        for (int i = 2;; i++) {
            if (n % i == 0) {
                n /= i;
                v.push_back(i);
                break;
            }
        }
    }
    return v;
}
 
bool Factorial(int n) {
    //--------------------------------------------
    auto nPw = Primes(n*n); //now nPw contains all prime dividers of n^2
    //--------------------------------------------
    vector<int> values; values.reserve(n); //Vector values for storing all dividers of n!
    //--------------------------------------------
    for (int i = 1; i <= n; i++) {
        auto tmp = Primes(i);   //tmp stores all dividers of i
        move(tmp.begin(), tmp.end(), inserter(values, values.end()));   //moving tmp to the end of the values
    }
    //--------------------------------------------
    //Deleting all common dividers of n^2 and n!
    for(int i = 0; i != nPw.size(); i++){
        for(int j = 0; j != values.size(); j++){
            if (nPw[i] == values[j]) {
                nPw.erase(nPw.begin() + i);
                values.erase(values.begin() + j);
                i--; j--;
                break;
            }
        }
    }
    //--------------------------------------------
    return nPw.size() == 0;
}
Но проходит только половину тестов, все остальное - исчерпан лимит времени, а последнее, так вообще ошибка исполнения.
Подскажите, плиз, как можно оптимизировать алгоритм и почему, как вы думаете, вылазит ошибка?
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
08.01.2020, 23:35
Ответы с готовыми решениями:

Оптимизировать решение задачи о “двоичных строках”
Условия задачи на приложенном скрине. Решение не проходит по времени несколько последних коварных тестов, очень-очень долго считает для n и...

Как можно оптимизировать программу?
Здравствуй, Cyberforum. Долго копался в коде, но не вхожу во время) #include &lt;bits/stdc++.h&gt; using namespace std; ...

Как можно оптимизировать код?
#include &lt;iostream&gt; #include &lt;algorithm&gt; #include &lt;vector&gt; using namespace std; int main() { int n, temp; cin&gt;&gt;n; ...

28
Диссидент
Эксперт C
 Аватар для Байт
27714 / 17332 / 3810
Регистрация: 24.12.2010
Сообщений: 38,978
08.01.2020, 23:58
Цитата Сообщение от NuMeRiC_ Посмотреть сообщение
Подскажите, плиз,
Идея правильная. Но вам достаточно выяснить, делится ли (n-1)! на n.
И совершенно не нужно делать вектор из самих делителей. Достаточно запоминать количество каждого простого делителя в n. Анализируя делители (n-1)! надо просто вычитать из элементов получившегося вектора. Если остались положительные значения - не делится.
А возможно, лучше наоборот. Сначала составит вектор количеств делителей (n-1)! А затем пройтись по n. Получилось отрицательное - не делится.
0
Just Do It!
 Аватар для XLAT
4197 / 2652 / 654
Регистрация: 23.09.2014
Сообщений: 8,946
Записей в блоге: 3
09.01.2020, 09:22
NuMeRiC_,
проверьте у себя в валидаторе:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
 
typedef long long UINT;
 
bool foo(int n)
{   UINT r = 1;
    UINT nn = n * n;
    for(UINT i = 1; i < n; i++)
    {   r *= i;
        if(r%nn == 0) return true;
        r %= nn;
    }
    return false;
}
 
int main()
{   
    UINT start = 1000000000;
    for(UINT n = start; n < start + 30; n++)
    {   
        ///-----------------------------------|
        /// Ручной ввод.                      |
        ///-----------------------------------:
        /// std::cout << "n = "; std::cin >> n;
        
        std::cout << n << ":\t" << (foo(n) ? "TRUE" : "FALSE") << "\n";
    }
}
1
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
09.01.2020, 09:38
Лучший ответ Сообщение было отмечено Байт как решение

Решение

Цитата Сообщение от NuMeRiC_ Посмотреть сообщение
Подскажите, плиз, как можно оптимизировать алгоритм и почему, как вы думаете, вылазит ошибка?
Подсказываю - читать Кнута от корки до корки. Вернее конкретно здесь нужна глава про модулярную арифметику, но читать от корки до корки все равно. Кратко говоря:
(A*B) mod C=((A mod C)*(B mod C)) mod C
Проще говоря, если вам нужно найти остаток от деления на C, то вы можете все сомножители урезать до X mod C (в нашем случае C=N*N). Что там у них в старших разрядах было - наплевать, на конечный результат это не влияет. А раз можно сомножители урезать, можно считать большие факториалы, не паря мозг арифметическими переполнениями.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
#include<iostream>
 
int main()
{
    unsigned long n;
    std::cin>>n;
    const unsigned long mod=n*n;
    unsigned long fact=1;
    for(unsigned long i=1;i<=n;++i)
        fact=(fact*i)%mod;
    std::cout<<(fact?"false":"true")<<std::endl;
    return 0;
}
3
Just Do It!
 Аватар для XLAT
4197 / 2652 / 654
Регистрация: 23.09.2014
Сообщений: 8,946
Записей в блоге: 3
09.01.2020, 09:58
Цитата Сообщение от XLAT Посмотреть сообщение
for(UINT i = 1; i < n; i++)
поправил:
C++
1
2
3
4
5
6
7
8
9
10
11
bool foo(int n)
{   UINT r = 1;
    UINT nn = n * n;
    for(UINT i = 1; i <= n; i++)
    {   r *= i;
        r %= nn;
        if(r == 0) return true;
        
    }
    return false;
}
моё предположение в том, что весь факториал не обязательно вычислять,
если промежуточный результат будет равен нулю.
2
Диссидент
Эксперт C
 Аватар для Байт
27714 / 17332 / 3810
Регистрация: 24.12.2010
Сообщений: 38,978
09.01.2020, 10:16
XLAT, вы хоть примерно представляете, с какого порядка числами придется иметь дело?
Цитата Сообщение от XLAT Посмотреть сообщение
весь факториал не обязательно вычислять, если промежуточный результат будет равен нулю.
Такая ситуация будет встречаться очень нечасто. Значит - не вылечит. И ТС об этом обмолвился
Цитата Сообщение от NuMeRiC_ Посмотреть сообщение
(если делать по-простому, то есть считать каждое выражение), это примерно 20-25. Дальше все.
0
 Аватар для COKPOWEHEU
4081 / 2679 / 432
Регистрация: 09.09.2017
Сообщений: 11,897
09.01.2020, 10:21
А если делить на факториал на квадрат, а наоборот, квадрат на множители? С учетом, что n! делится на n, и n2 делится на n, достаточно чтобы n было представимо как произведение (1...n):
C
1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
 
int main(){
  int n, nn;
  scanf("%i", &n);
  nn = n;
  for(int i=n-1; i>1; i--){
    if((nn % i) == 0)nn/=i;
    if(nn == 1)break;
  }
  printf("%i\n", (nn==1));
}
Добавлено через 4 минуты
UPD: мой код не работает на n=9, буду думать дальше
0
Диссидент
Эксперт C
 Аватар для Байт
27714 / 17332 / 3810
Регистрация: 24.12.2010
Сообщений: 38,978
09.01.2020, 10:28
Renji, Отличное решение! Почему-то вчера вечером не допер...
Цитата Сообщение от Renji Посмотреть сообщение
читать Кнута от корки до корки.
хотя вот этого при всем своем уважении к автору, я бы советовать не стал.
Цитата Сообщение от Renji Посмотреть сообщение
Кратко говоря:
операция "%" является гомоморфизмом. Понять, что это такое и использовать в жизни много проще, чем обдирать кору с Кнута.

Добавлено через 1 минуту
Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
буду думать дальше
стоит ли? Прекрасное решение уже найдено в посте 4
0
 Аватар для COKPOWEHEU
4081 / 2679 / 432
Регистрация: 09.09.2017
Сообщений: 11,897
09.01.2020, 10:32
Скорее всего, делить надо не на сомножитель факториала, а на его делители:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
 
int gcd(int a, int b){
  while(a > 0 && b > 0){
    if(a > b){
      a %= b;
    }else{
      b %= a;
    }
  }
 return a + b;
}
 
int main(){
  int n;
  scanf("%i", &n);
  for(int i=n-1; i>1; i--){
    n /= gcd(n, i);
    if(n == 1)break;
  }
  printf("%i\n", (n==1));
}
0
Диссидент
Эксперт C
 Аватар для Байт
27714 / 17332 / 3810
Регистрация: 24.12.2010
Сообщений: 38,978
09.01.2020, 10:32
NuMeRiC_, мое решение в принципе тоже можно было бы довести до ума. Но - не стоит. Оно вполне громоздко, тяжело изложено и трудоемко. В то время как решение уважаемого Renji просто, элегантно, естественно и реализуется в 3 строки.
0
Just Do It!
 Аватар для XLAT
4197 / 2652 / 654
Регистрация: 23.09.2014
Сообщений: 8,946
Записей в блоге: 3
09.01.2020, 10:34
Цитата Сообщение от Байт Посмотреть сообщение
Такая ситуация будет встречаться очень нечасто.
:лол

вы хотя бы тест хоть какой-нибудь запустили, чтобы это утверждать.

Цитата Сообщение от Renji Посмотреть сообщение
Подсказываю - читать Кнута от корки до корки
Кнут не нужен, просто нужно думать как Кнут.
0
Диссидент
Эксперт C
 Аватар для Байт
27714 / 17332 / 3810
Регистрация: 24.12.2010
Сообщений: 38,978
09.01.2020, 10:39
COKPOWEHEU, Да глупости все это!
C++
1
2
3
4
5
6
7
r = 1;
for(k=2; k<n; k++) {
  r = r*k%n;
  if (r==0) break;
}
if (r==0) cout << "Yes";
else cout << "No":
И ЭТО ВСЕ!!!
(скопипастено у Renji, )

Добавлено через 2 минуты
Цитата Сообщение от XLAT Посмотреть сообщение
вы хотя бы тест хоть какой-нибудь запустили, чтобы это утверждать.
Зачем запускать какие-то тесты? Стобы убедиться в том, что 100! достаточно большое число? Если вам это еще не очевидно, вы и запускайте...
0
Just Do It!
 Аватар для XLAT
4197 / 2652 / 654
Регистрация: 23.09.2014
Сообщений: 8,946
Записей в блоге: 3
09.01.2020, 10:41
Цитата Сообщение от Байт Посмотреть сообщение
if (r==0) break;
а как же ваше:
Цитата Сообщение от Байт Посмотреть сообщение
Такая ситуация будет встречаться очень нечасто
???
1
Диссидент
Эксперт C
 Аватар для Байт
27714 / 17332 / 3810
Регистрация: 24.12.2010
Сообщений: 38,978
09.01.2020, 10:55
XLAT, да, приношу извинения. Не заметил, что r %= nn. Процентик пропустил. Бес попутал.
1
Just Do It!
 Аватар для XLAT
4197 / 2652 / 654
Регистрация: 23.09.2014
Сообщений: 8,946
Записей в блоге: 3
09.01.2020, 11:27
Цитата Сообщение от Байт Посмотреть сообщение
решение уважаемого Renji просто, элегантно, естественно и реализуется в 3 строки
Мысль эскимоса:
на самом деле, как видно из первопоста, это ещё не является решением.
я имею ввиду в плане оптимизации.

Вот такая ситуация:
на вход функции foo(n) будут подаваться разные n из некоторого рандомного множества.
Что будет происходить:
Наш факториал будет вычисляться повторно каждый раз!

А нужно:
при повторном вычислении функция должна использовать уже вычисленные факториалы
из организованного хранилища.


автор топика об этом не упоминает явно,
поэтому это просто моя догадка.
0
 Аватар для COKPOWEHEU
4081 / 2679 / 432
Регистрация: 09.09.2017
Сообщений: 11,897
09.01.2020, 12:45
А нужно ли? Сложность алгоритма и так невелика. На кеширование результатов (кстати, каких? Хранить факториал целиком нельзя, он слишком большой, а остатки от деления факториалов зависят от входного числа и будут всегда разными) потребуется память, то есть даже в случае выигрыша по скорости (кстати, не факт что он вообще будет: умножение не такая дорогая операция) будет проигрыш по памяти.
А самое главное - как код будет оформлен в конечном итоге. Мне, например, представляется скорее в виде отдельной утилиты, запускаемой для каждого числа заново. Оно и в формулировке ТСа скорее всего так будет, у него ведь автоматизированное тестирование.
0
Just Do It!
 Аватар для XLAT
4197 / 2652 / 654
Регистрация: 23.09.2014
Сообщений: 8,946
Записей в блоге: 3
09.01.2020, 13:40
Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
А нужно ли?
ещё как можно ускорить!
особенно, если множество n не рандомное,
а некий диапазон, причем сортированный.

Запустите мой тест и увидите на секундомере сколько нужно время для 30 n.
Особенно, когда идет вычисление с результатом false.


И не нужно ни какой дополнительной памяти,
кроме 8 байт для last n (последнего вычисленного факториала).

я не стал это делать, потому про это у автора сказано всего лишь,
что нужна некая оптимизация, т.е. это как некий намёк и не более.

Но то что я предложил, уж слишком просто, чтобы быть окончательным решением.

Добавлено через 14 минут
ps:
нет, это здесь, не прокатит.
0
 Аватар для COKPOWEHEU
4081 / 2679 / 432
Регистрация: 09.09.2017
Сообщений: 11,897
09.01.2020, 14:49
Цитата Сообщение от XLAT Посмотреть сообщение
Запустите мой тест и увидите на секундомере сколько нужно время для 30 n.
Проверил. С типом данных int ваш вариант работает быстрее. Ннапример, на последовательности (0...20000) он отработал за 2.2 сек, а мой за 3.3. Кстати, в моем коде стоило поменять направление перебора: не от большего к меньшему, а наоборот, тогда время чуть снижается до 3.1 сек
Но ваш вариант переполняется, например, на n=1318.
0
Just Do It!
 Аватар для XLAT
4197 / 2652 / 654
Регистрация: 23.09.2014
Сообщений: 8,946
Записей в блоге: 3
09.01.2020, 15:33
Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
Но ваш вариант переполняется
не может быть, если на 1000000000 не переполняется!

COKPOWEHEU,
ладно, ладно
вот тут я же поправлялся:
пост(мой) номер 5

Добавлено через 44 секунды
вместо
i < n;
нужно
i <= n;
0
 Аватар для COKPOWEHEU
4081 / 2679 / 432
Регистрация: 09.09.2017
Сообщений: 11,897
09.01.2020, 18:30
Цитата Сообщение от XLAT Посмотреть сообщение
вместо
i < n;
нужно
i <= n;
Но там в цикле и так i<=n, в том виде я и копировал.
Во избежание недоразумений проверьте до n=2800 и выложите код, который точно не переполняется. Для поиска подозрительных мест можно сравнивать с моим вариантом. Во-первых, там нечему переполняться, поскольку нет сложений и умножений, а во-вторых, подозрительные места на то и подозрительные чтобы проверять вручную.
Кстати, мне тут в голову пришло, что условие ТСа может оказаться эквивалентным проверке числа (n-1) на простоту. А ведь это тоже вполне стандартная задача.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
09.01.2020, 18:30
Помогаю со студенческими работами здесь

Как можно оптимизировать данный код?
И... Ещё один вопрос: Дан участок кода С++: #include &lt;iostream&gt; #include &quot;Windows.h&quot; using namespace...

Можно как-то оптимизировать этот код?
#include &lt;iostream&gt; using namespace std; int main() { unsigned int num, trueNum, a, howMany, endwrite, fail; fail = 0; ...

Как можно еще оптимизировать код?
Как еще можно оптимизировать данный код? Если вкратце, то он выводит значение АВ, если ключ = вводу пользотвателя. #include...

Как понять условие данной задачи?
Вводится одно натуральное число, не более 31. Напишите программу, которая делает то же самое, что и в примерах входных и выходных данных....

Как составить формулу для данной задачи
Задание такое: Дано поле 8x8: |12345678 -+-------- 1|00000/00 2|*000/000 3|0\0/0000 4|00+00000


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
Thinkpad X220 Tablet — это лучший бюджетный ноутбук для учёбы, точка.
Programma_Boinc 23.12.2025
Thinkpad X220 Tablet — это лучший бюджетный ноутбук для учёбы, точка. Рецензия / Мнение/ Перевод https:/ / **********/ gallery/ thinkpad-x220-tablet-porn-gzoEAjs . . .
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Как объединить две одинаковые БД Access с разными данными
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru