#python #алгоритм
Пояснение: Есть натуральное число x, его можно представить как произведение двух натуральных чисел m и n (x=m*n). Далее x нужно заменить на m+n-2 (x=m+n-2). И это все необходимо выполнять, пока x не станет равно 1 x может быть в пределах миллиарда Пример: В программу поступило число 6. 6=3*2 3+2-2=3 6=6*1 6+1-2=5 3=3*1 3+1-2=2 5=5*1 5+1-2=4 2=2*1 2+1-2=1 - x=1, останавливаемся (до единицы мы дошли за 3 замены) 4=2*2 2+2-2=2 Мои попытки from math import sqrt def isint(n): return not(n%1) def f(n): count = 0 while(True): x=sqrt(n) if(n<=1): return count if(isint(x)): n=x+x-2 else: x=int(x) while(isint(n/x)==False): x=x-1 if(x<=0): return count n=int((n/x)+x-2) count+=1 print('Минимальное кол-во попыток для числа 6 = ',f(6)) P.S. Мой алгоритм делает ошибки, но некоторые числа он все же считает
Ответы
Ответ 1
Я питон не знаю... Но на С++ решение этой задачи - вот: #include#include #include using namespace std; const int MAXSIZE = 1000000000; unsigned char m[MAXSIZE+1]; int count(int x, int level = 0, int curmin = MAXSIZE) { if (level > curmin) return -1; // Отсечение - нет смысла лезть вглубь // при наличии более короткого решения if (x == 1) return 0; if (m[x]) { return m[x]; // Сохраненное значение } int res = MAXSIZE; for(int i = sqrt(x)+0.1; i >= 1; --i) { if (x%i) continue; int k = count(i+x/i-2, level+1, res); if (k < 0) continue; if (k < res) { res = k; } } return m[x] = res+1; }; int main(int argc, const char * argv[]) { int n = (argc > 1) ? atoi(argv[1]) : MAXSIZE; if (n > MAXSIZE) n = MAXSIZE; cout << n << ": " << count(n) << endl; int cur = m[n]; while (n > 1) { for(int i = 1; i*i <= n; ++i) { if (n%i) continue; if (m[i+n/i-2] == cur-1) { n = i+n/i-2; --cur; cout << i << " "; break; } } } cout << endl; } Как видите, на ideone решило миллиард за 0.04с :) Ключевые моменты: 1. Перебор с отсечением 2. Начинаем со значений, близких к корню 3. Мемоизация Кстати, до миллиона включительно самая длинная цепочка - 11, так что глубокой рекурсии явно не стоит ожидать. Ответ 2
Ваш алгоритм основан на предположении, что если a < b, то f(a) <= f(b), но это не всегда так, например f(7) = 4, а f(8) = 3. Для начала, думаю, стоит начать с полного перебора, если ничего лучше не придумаете, то добавьте мемоизацию. Вот простой пример полного рекурсивного перебора на C#: static int GetStepsCount(int x) { // Граничное условие выхода if (x <= 1) return 0; // Делаем один шаг и прибавляем к нему return 1 + Enumerable // Для всех m от 1 до корня из x .Range(1, (int)Math.Sqrt(x)) // Где m является делителем x .Where(m => x % m == 0) // Вычислить GetStepsCount .Select(m => GetStepsCount(m + x / m - 2)) // И взять среди них минимум .Min(); } Если алгоритм требуется выполнять для входных чисел бОльших, чем пару сотен, то мемоизация неизбежна, т.к. полный перебор работает очень долго. Вот пример, с использованием словаря: static Dictionarymemory = new Dictionary (); static int GetStepsCount(int x) { if (x <= 1) return 0; if (!memory.ContainsKey(x)) memory[x] = 1 + Enumerable .Range(1, (int)Math.Sqrt(x)) .Where(m => x % m == 0) .Select(m => GetStepsCount(m + x / m - 2)) .Min(); return memory[x]; } Расход памяти небольшой, но скорость выполнения возрастает значительно. Ответ 3
import functools from math import sqrt @functools.lru_cache() def f(x): if x <=1: return 0 return 1 + min([ f(m + x // m - 2) for m in range(1,int(sqrt(x))+1) if x%m ==0]) Это сразу с кэшированием ;-) Так что єто Пайтон подрос получается ))) кстати, f(1024) дает 7. Без @functools.lru_cache() считается минут 20, а с @functools.lru_cache() - 2-3 секунды !!
Комментариев нет:
Отправить комментарий