我有一个号码n
,我希望找到三个号码,其产品为n
,但尽可能彼此接近。也就是说,如果n = 12
那么我希望得到3,2,2,而不是6,1,2。
另一种想到它的方法是,如果n
是长方体的体积,那么我想找到两边的长度,以便使长方体尽可能像立方体一样(也就是说,长度尽可能相似)。这些数字必须是整数。
我知道不太可能有一个完美的解决方案,而且我很乐意使用能够在大多数时候给出一个好答案的东西,但是我想不出去想出这个算法。有什么想法吗?
答案 0 :(得分:11)
这是我的第一个算法草图,授予n
相对较小:
n
的{{3}}。f1
,f2
,f3
。如果少于三个因素,请指定1
。修改的
我们来看n=60
。
其主要因素是5 3 2 2
。
设置f1=5
,f2=3
和f3=2
。
剩余的2
乘以f3
,因为它是最小的。
我们最终得到5 * 4 * 3 = 60
。
修改的
此算法无法找到最佳通知prime factors注释:
考虑17550 = 2 * 3 * 3 * 3 * 5 * 5 * 13.当最佳值为25,26,27时,您的算法将给出15,30,39。
修改的
好的,这是我的第二个算法草图,启发式稍微好一些:
L
设置为n
的{{3}}。r
设置为n
的{{3}}。F
,最初设置为1
。L[i]
与每个因子相乘,然后按降序排列。
r
,请执行乘法并继续前进到下一个
主要因素。F
。如果超出F
s,则乘以最小值。 这适用于17550
:
n=17550
L=13,5,5,3,3,3,2
r=25.98
F = { 1, 1, 1 }
迭代1:
F[0] * 13
小于r
,将F
设为{13,1,1}
。迭代2:
F[0] * 5 = 65
比r
更受欢迎。F[1] * 5 = 5
小于r
,将F
设为{13,5,1}
。迭代3:
F[0] * 5 = 65
比r
更受欢迎。F[1] * 5 = 25
小于r
,将F
设为{13,25,1}
。迭代4:
F[0] * 3 = 39
比r
更受欢迎。F[1] * 3 = 75
比r
更受欢迎。F[2] * 3 = 3
小于r
,将F
设为{13,25,3}
。迭代5:
F[0] * 3 = 39
比r
更受欢迎。F[1] * 3 = 75
比r
更受欢迎。F[2] * 3 = 9
小于r
,将F
设为{13,25,9}
。迭代6:
F[0] * 3 = 39
比r
更受欢迎。F[1] * 3 = 75
比r
更受欢迎。F[2] * 3 = 27
大于r
,但它是我们可以获得的最小F.将F
设为{13,25,27}
。迭代7:
F[0] * 2 = 26
比r
更受欢迎,但它是我们能得到的最小的F.将F
设为{26,25,27}
。答案 1 :(得分:3)
这是一种纯粹基于数学的方法,它返回最佳解决方案,不涉及任何类型的排序。天啊,它甚至不需要素数因素。
1)回想一下多项式
根的总和和乘积由
给出
其中x_i
是根。
2)回顾优化理论的另一个基本结果:
,即给定两个变量使得它们的乘积是常数,当两个变量彼此相等时,总和是最小的。波浪变量表示最佳值。
这样做的必然结果是,如果产品是常数的两个变量的总和是最小的,那么这两个变量就彼此相等。
现在可以将上述问题重新表述为多项式根寻找练习。我们将构造一个满足条件的多项式,并且该多项式的根将是你的答案。如果您需要k
个最佳数字,则您将获得度k
的多项式。在这种情况下,我们可以用三次方程来讨论
我们知道:
c
是输入数字的负数(假设为正数)a
是一个整数且为负(因为因子都是正数)b
是一个整数(它是一次取两个根的总和)并且是正数。p
的根源必须是真实的(并且是积极的,但这已经得到了解决)。要解决这个问题,我们只需要根据上述条件最大化a
。目前尚未明确知道的唯一部分是条件4,我们可以使用discriminant of the polynomial轻松强制执行。
对于三次多项式p
,判别式为
和p
如果∆>0
具有真实且不同的根,并且∆=0
具有真实和重合(两个或全部三个)。因此,约束4现在读取∆>=0
。现在这很简单,也很容易编程。
这是Mathematica中实现此目的的解决方案。
这是对其他答案/评论中使用的一些数字的测试。
左侧的列是列表,右侧列中的相应行给出了最佳解决方案。
我只是注意到OP从来没有提到3个数字需要是整数,尽管每个人(包括我自己直到现在)都认为他们是(可能是因为他的第一个例子)。重新阅读问题,并通过立方体示例,似乎OP不是整数。
这是一个重要的观点,它将决定追求和需要定义哪类算法。如果它们不必是整数,则可以提供几种基于多项式的解,其中一种是我的(在放宽整数约束之后)。如果它们应该是整数,那么使用分支-n-bound / branch-n-cut /切割平面的方法可能更合适。
以下是假设OP意味着三个数字是整数。
我现在实现它的方式,在某些情况下它可以提供非整数解决方案。
这为x
提供非整数解决方案的原因是因为我只实现了最大化a
,实际上,b
也需要最小化(不仅如此,还因为我没有对x_i
整数设置约束。可以使用整数根定理,这将涉及找到素数因子,但会使事情变得更复杂。)< / p>
文本中的Mathematica代码
Clear[poly, disc, f]
poly = x^3 + a x^2 + b x + c;
disc = Discriminant[poly, x];
f[n_Integer] :=
Module[{p, \[CapitalDelta] = disc /. c -> -n},
p = poly /.
Maximize[{a, \[CapitalDelta] >= 0,
b > 0 && a < 0 && {a, b} \[Element] Integers}, {a, b}][[
2]] /. c -> -n;
Solve[p == 0]
]
答案 2 :(得分:2)
正如Anders Lindahl所追求的,可能有一种聪明的方法可以找到最紧密的三重奏,但我会专注于一种更基本的方法。
如果我生成所有三元组,那么我可以随后过滤它们,但我会从那里开始。我知道生成这些的最好方法是使用递归:
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}}
使用上面给出的函数/算法的输出,然后可以测试三元组的质量,无论如何。
请注意,由于排序,输出中的最后一个三元组是具有最小因子的最大三元组。这通常是结果中最“立方”的,但偶尔也不是。
如果必须找到真正的最佳值,那么从列表的右侧开始测试是有意义的,如果没有快速找到更好的结果则放弃搜索,因为当您向左移动时结果的质量会降低。 / p>
显然这个方法依赖于快速Divisors
函数,但我认为这是一个标准的库函数,或者你可以在StackOverflow上找到一个很好的实现。有了这个,这应该是非常快的。上面的代码在我的机器上找到了1.2秒内n为1到10,000的所有三元组。
答案 3 :(得分:1)
修改强> 以下是使用更高效代码的简短解释,KSetPartitions大大简化了事情。那么Mr.W.的一些建议也是如此。整体逻辑保持不变。
假设 n 至少有3个素因子,
KSetPartitions
列表。以下是Mathematica中的代码:
Needs["Combinatorica`"]
g[n_] := Module[{factors = Join @@ ConstantArray @@@ FactorInteger[n]},
Sort[Union[Sort /@ Apply[Times, Union[Sort /@
KSetPartitions[factors, 3]], {2}]]
/. {a_Integer, b_Integer, c_Integer} :>
{Total[Power[{a, b, c}, 2]], {a, b, c}}][[1, 2]]]
它可以处理相当大的数字,但随着n的因子数量的增加而显着减慢。以下示例显示240,2400,...... 24000000的时间。 通过考虑一个主要因子在除数中出现不止一次的情况,原则上可以加快这一点。我还没有专业知识。
In[28]:= g[240]
Out[28]= {5, 6, 8}
In[27]:= t = Table[Timing[g[24*10^n]][[1]], {n, 6}]
Out[27]= {0.001868, 0.012734, 0.102968, 1.02469, 10.4816, 105.444}
答案 4 :(得分:1)
不应重新发明轮子,而应将其视为众所周知的 NP-complete 问题的变体。
n
。鉴于多处理器调度问题是NP完全的,难怪找到一个不搜索整个问题空间并找到最佳解决方案的算法很难。
但我想已经有几种算法可以处理Bin-Packing或Multiprocessor-Scheduling,并以有效的方式找到接近最优的解决方案。
另一个相关问题(概括)是 Job shop scheduling 。请参阅维基百科描述,其中包含许多已知算法的链接。
维基百科描述为 (经常使用的LPT算法(最长处理时间) )正是Anders Lindahl首先提出的。