IntegerPartition 的变体?

发布于 2024-10-18 02:44:35 字数 314 浏览 3 评论 0原文

IntegerPartitions[n, {3, 10}, Prime ~Array~ 10]

在 Mathematica 中,这将给出获取 n 作为前 10 个素数中的 3 到 10 之和的所有方法的列表,并允许根据需要重复。

如何有效地找到等于 n 的总和,并允许每个元素仅使用一次

使用前十个素数只是一个玩具示例。我寻求一个对任意参数都有效的解决方案。在实际情况中,即使使用多项式系数,生成所有可能的和也会占用太多内存。

我忘了注明我正在使用 Mathematica 7。

IntegerPartitions[n, {3, 10}, Prime ~Array~ 10]

In Mathematica this will give a list of all the ways to get n as the sum of from three to ten of the first ten prime numbers, allowing duplicates as needed.

How can I efficiently find the sums that equal n, allowing each element to only be used once?

Using the first ten primes is only a toy example. I seek a solution that is valid for arbitrary arguments. In actual cases, generating all possible sums, even using polynomial coefficients, takes too much memory.

I forgot to include that I am using Mathematica 7.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

睡美人的小仙女 2024-10-25 02:44:35

下面将构建一棵二叉树,然后对其进行分析并提取结果:

Clear[intParts];
intParts[num_, elems_List] /; Total[elems] < num := p[];
intParts[num_, {fst_, rest___}] /; 
   fst < num := {p[fst, intParts[num - fst, {rest}]], intParts[num, {rest}]};
intParts[num_, {fst_, rest___}] /; fst > num := intParts[num, {rest}];
intParts[num_, {num_, rest___}] := {pf[num], intParts[num, {rest}]};


Clear[nextPosition];
nextPosition = 
  Compile[{{pos, _Integer, 1}},
     Module[{ctr = 0, len = Length[pos]},
       While[ctr < len && pos[[len - ctr]] == 1, ++ctr];
       While[ctr < len && pos[[len - ctr]] == 2, ++ctr];
       Append[Drop[pos, -ctr], 1]], CompilationTarget -> "C"];

Clear[getPartitionsFromTree, getPartitions];
getPartitionsFromTree[tree_] :=
  Map[Extract[tree, #[[;; -3]] &@FixedPointList[nextPosition, #]] &, 
     Position[tree, _pf, Infinity]] /. pf[x_] :> x;
getPartitions[num_, elems_List] := 
    getPartitionsFromTree@intParts[num, Reverse@Sort[elems]];

例如,

In[14]:= getPartitions[200,Prime~Array~150]//Short//Timing

Out[14]= {0.5,{{3,197},{7,193},{2,5,193},<<4655>>,{3,7,11,13,17,19,23,29,37,41},      
       {2,3,5,11,13,17,19,23,29,37,41}}}

这并不是非常快,也许算法可以进一步优化,但至少分区数量不会像<代码>整数分区。

编辑:

有趣的是,简单的记忆将解决方案的速度提高了大约两倍,在我之前使用的示例中:

Clear[intParts];
intParts[num_, elems_List] /; Total[elems] < num := p[];
intParts[num_, seq : {fst_, rest___}] /; fst < num := 
    intParts[num, seq] = {p[fst, intParts[num - fst, {rest}]], 
          intParts[num, {rest}]};
intParts[num_, seq : {fst_, rest___}] /; fst > num := 
    intParts[num, seq] = intParts[num, {rest}];
intParts[num_, seq : {num_, rest___}] := 
    intParts[num, seq] = {pf[num], intParts[num, {rest}]};

现在,

In[118]:= getPartitions[200, Prime~Array~150] // Length // Timing

Out[118]= {0.219, 4660}

The following will build a binary tree, and then analyze it and extract the results:

Clear[intParts];
intParts[num_, elems_List] /; Total[elems] < num := p[];
intParts[num_, {fst_, rest___}] /; 
   fst < num := {p[fst, intParts[num - fst, {rest}]], intParts[num, {rest}]};
intParts[num_, {fst_, rest___}] /; fst > num := intParts[num, {rest}];
intParts[num_, {num_, rest___}] := {pf[num], intParts[num, {rest}]};


Clear[nextPosition];
nextPosition = 
  Compile[{{pos, _Integer, 1}},
     Module[{ctr = 0, len = Length[pos]},
       While[ctr < len && pos[[len - ctr]] == 1, ++ctr];
       While[ctr < len && pos[[len - ctr]] == 2, ++ctr];
       Append[Drop[pos, -ctr], 1]], CompilationTarget -> "C"];

Clear[getPartitionsFromTree, getPartitions];
getPartitionsFromTree[tree_] :=
  Map[Extract[tree, #[[;; -3]] &@FixedPointList[nextPosition, #]] &, 
     Position[tree, _pf, Infinity]] /. pf[x_] :> x;
getPartitions[num_, elems_List] := 
    getPartitionsFromTree@intParts[num, Reverse@Sort[elems]];

For example,

In[14]:= getPartitions[200,Prime~Array~150]//Short//Timing

Out[14]= {0.5,{{3,197},{7,193},{2,5,193},<<4655>>,{3,7,11,13,17,19,23,29,37,41},      
       {2,3,5,11,13,17,19,23,29,37,41}}}

This is not insanely fast, and perhaps the algorithm could be optimized further, but at least the number of partitions does not grow as fast as for IntegerPartitions.

Edit:

It is interesting that simple memoization speeds the solution up about twice on the example I used before:

Clear[intParts];
intParts[num_, elems_List] /; Total[elems] < num := p[];
intParts[num_, seq : {fst_, rest___}] /; fst < num := 
    intParts[num, seq] = {p[fst, intParts[num - fst, {rest}]], 
          intParts[num, {rest}]};
intParts[num_, seq : {fst_, rest___}] /; fst > num := 
    intParts[num, seq] = intParts[num, {rest}];
intParts[num_, seq : {num_, rest___}] := 
    intParts[num, seq] = {pf[num], intParts[num, {rest}]};

Now,

In[118]:= getPartitions[200, Prime~Array~150] // Length // Timing

Out[118]= {0.219, 4660}
不羁少年 2024-10-25 02:44:35

可以使用求解整数,乘数限制在 0 和 1 之间。我将展示一个具体示例(前 10 个素数,加到 100),但为此制定通用过程很容易。

primeset = Prime[Range[10]];
mults = Array[x, Length[primeset]];
constraints01 = Map[0 <= # <= 1 &, mults];
target = 100;

Timing[res = mults /. 
  Solve[Flatten[{mults.primeset == target, constraints01}],
    mults, Integers];
  Map[Pick[primeset, #, 1] &, res]
 ]

输出[178]= {0.004, {{7, 11, 13, 17, 23, 29},
{5, 11, 13, 19, 23, 29}, {5, 7, 17, 19, 23, 29},
{2, 5, 11, 13, 17, 23, 29}, {2, 3, 11, 13, 19, 23, 29},
{2, 3, 7, 17, 19, 23, 29}, {2, 3, 5, 7, 11, 13, 17, 19, 23}}}

---编辑---
要在版本 7 中执行此操作,需要使用“Reduce”而不是“Solve”。我会将其捆绑在一个函数中。

knapsack[target_, items_] := Module[
  {newset, x, mults, res},
  newset = Select[items, # <= target &];
  mults = Array[x, Length[newset]];
  res = mults /.
    {ToRules[Reduce[
       Flatten[{mults.newset == target, Map[0 <= # <= 1 &, mults]}],
       mults, Integers]]};
  Map[Pick[newset, #, 1] &, res]]

这是 Leonid Shifrin 的示例:

Timing[Length[knapsack[200, Prime[Range[150]]]]]

Out[128]= {1.80373, 4660}

不如树代码快,但仍然(我认为)合理的行为。至少,不是明显不合理。

---编辑结束---

Daniel Lichtblau
沃尔夫勒姆研究公司

Can use Solve over Integers, with multipliers constrained between 0 and 1. I'll show for a specific example (first 10 primes, add to 100) but it is easy to make a general procedure for this.

primeset = Prime[Range[10]];
mults = Array[x, Length[primeset]];
constraints01 = Map[0 <= # <= 1 &, mults];
target = 100;

Timing[res = mults /. 
  Solve[Flatten[{mults.primeset == target, constraints01}],
    mults, Integers];
  Map[Pick[primeset, #, 1] &, res]
 ]

Out[178]= {0.004, {{7, 11, 13, 17, 23, 29},
{5, 11, 13, 19, 23, 29}, {5, 7, 17, 19, 23, 29},
{2, 5, 11, 13, 17, 23, 29}, {2, 3, 11, 13, 19, 23, 29},
{2, 3, 7, 17, 19, 23, 29}, {2, 3, 5, 7, 11, 13, 17, 19, 23}}}

---edit---
To do this in version 7 one would use Reduce instead of Solve. I'll bundle this in one function.

knapsack[target_, items_] := Module[
  {newset, x, mults, res},
  newset = Select[items, # <= target &];
  mults = Array[x, Length[newset]];
  res = mults /.
    {ToRules[Reduce[
       Flatten[{mults.newset == target, Map[0 <= # <= 1 &, mults]}],
       mults, Integers]]};
  Map[Pick[newset, #, 1] &, res]]

Here is Leonid Shifrin's example:

Timing[Length[knapsack[200, Prime[Range[150]]]]]

Out[128]= {1.80373, 4660}

Not as fast as the tree code, but still (I think) reasonable behavior. At least, not obviously unreasonable.

---end edit---

Daniel Lichtblau
Wolfram Research

叹倦 2024-10-25 02:44:35

我想提出一个解决方案,其精神与 Leonid 的解决方案类似,但更短且内存消耗更少。该代码不是构建树并对其进行后处理,而是遍历树并在找到解决方案时Sow

Clear[UniqueIntegerParitions];
UniqueIntegerParitions[num_Integer?Positive, 
  list : {__Integer?Positive}] := 
 Block[{f, $RecursionLimit = Infinity},
  f[n_, cv_, {n_, r___}] :=
   (Sow[Flatten[{cv, n}]]; f[n, cv, {r}];);
  f[n_, cv_, {m_, r___}] /; m > n := f[n, cv, {r}];
  f[n_, cv_, {m_, r___}] /; 
    Total[{r}] >= n - m := (f[n - m, {cv, m}, {r}]; f[n, cv, {r}]);
  f[___] := Null;
  Part[Reap[f[num, {}, Reverse@Union[Cases[list, x_ /; x <= num]]]], 
   2, 1]]

此代码比 Leonid 慢

In[177]:= 
UniqueIntegerParitions[200, Prime~Array~PrimePi[200]] // 
  Length // Timing

Out[177]= {0.499, 4660}

,但使用的内存大约少 6 倍,因此允许走得更远。

I would like to propose a solution, similar in spirit to Leonid's but shorter and less memory intensive. Instead of building the tree and post-processing it, the code walks the tree and Sows the solution when found:

Clear[UniqueIntegerParitions];
UniqueIntegerParitions[num_Integer?Positive, 
  list : {__Integer?Positive}] := 
 Block[{f, $RecursionLimit = Infinity},
  f[n_, cv_, {n_, r___}] :=
   (Sow[Flatten[{cv, n}]]; f[n, cv, {r}];);
  f[n_, cv_, {m_, r___}] /; m > n := f[n, cv, {r}];
  f[n_, cv_, {m_, r___}] /; 
    Total[{r}] >= n - m := (f[n - m, {cv, m}, {r}]; f[n, cv, {r}]);
  f[___] := Null;
  Part[Reap[f[num, {}, Reverse@Union[Cases[list, x_ /; x <= num]]]], 
   2, 1]]

This code is slower than Leonid's

In[177]:= 
UniqueIntegerParitions[200, Prime~Array~PrimePi[200]] // 
  Length // Timing

Out[177]= {0.499, 4660}

but uses about >~ 6 times less memory, thus allowing to go further.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文