Может быть умный способ найти самый плотный триплет, как Андерс Линдал преследует, но я остановлюсь на более базовом подходе.
Если я сгенерирую всех триплетов, тогдаЯ могу отфильтровать их потом, как захочу, поэтому начну с них.Наилучший из известных мне способов их генерации использует рекурсию:
f[n_, 1] := {{n}}
f[n_, k_] := Join @@
Table[
{q, ##} & @@@ Select[f[n/q, k - 1], #[[1]] >= q &],
{q, #[[2 ;; ⌈ Length@#/k ⌉ ]] & @ Divisors @ n}
]
Эта функция f
принимает два целочисленных аргумента: число для множителя n
и число факторов для получения k
.
Секция #[[2 ;; ⌈ Length@#/k ⌉ ]] & @ Divisors @ n
использует Divisors
для получения списка всех делителей n
(включая 1
), а затем берет из них второй (чтобы сбросить 1
)) до потолка числа делителей, деленного на k
.
Например, для {n = 240, k = 3}
вывод равен {2, 3, 4, 5, 6, 8}
Команда Table
выполняет итерации по этому списку, поканакапливая результаты, присваивая каждому элементу q
.
Тело Table
равно Select[f[n/q, k - 1], #[[1]] >= q &]
.Это вызывает f
рекурсивно, а затем выбирает из результата все списки, которые начинаются с номера >= q
.
{q, ##} & @@@
(также в теле), а затем «предваряют» q
к каждому из этихвыбранные списки.
Наконец, Join @@
объединяет списки этих выбранных списков, которые создаются каждым циклом Table
.
Результатом являются все целочисленные множителиn
на k
частей, в лексикографическом порядке.Пример:
In[]:= f[240, 3]
Out[]= {{2, 2, 60}, {2, 3, 40}, {2, 4, 30}, {2, 5, 24}, {2, 6, 20},
{2, 8, 15}, {2, 10, 12}, {3, 4, 20}, {3, 5, 16}, {3, 8, 10},
{4, 4, 15}, {4, 5, 12}, {4, 6, 10}, {5, 6, 8}}
С выходом функции / алгоритма, приведенным выше, можно затем протестировать триплеты на качество как угодно.
Обратите внимание, что из-за упорядочения последнего триплетана выходе тот, который имеет наибольший минимальный коэффициент.Обычно это будет самый «кубический» из результатов, но иногда это не так.
Если должен быть найден истинный оптимум, имеет смысл проверить, начиная с правой стороны списка, отказавшись от поиска.если лучший результат не может быть быстро найден, так как качество результатов уменьшается при перемещении влево.
Очевидно, что этот метод основан на быстрой Divisors
функции, но я предполагаю, что это либостандартная функция библиотеки, или вы можете найти хорошую реализацию здесь, на StackOverflow.С этим на месте это должно быть довольно быстро.Код выше находит все триплеты для n от 1 до 10000 за 1,26 секунды на моей машине.