我正在尝试为给定的H生成以下等式的所有解。
H = 4:
1) ALL solutions for x_1 + x_2 + x_3 + x_4 =4
2) ALL solutions for x_1 + x_2 + x_3 = 4
3) ALL solutions for x_1 + x_2 = 4
4) ALL solutions for x_1 =4
对于我的问题,总有4个方程要解决(独立于其他方程)。总共有2 ^(H-1)个解决方案。对于前一个,这是解决方案:
1) 1 1 1 1
2) 1 1 2 and 1 2 1 and 2 1 1
3) 1 3 and 3 1 and 2 2
4) 4
这是一个解决问题的R算法。
library(gtools)
H<-4
solutions<-NULL
for(i in seq(H))
{
res<-permutations(H-i+1,i,repeats.allowed=T)
resum<-apply(res,1,sum)
id<-which(resum==H)
print(paste("solutions with ",i," variables",sep=""))
print(res[id,])
}
然而,该算法进行的计算比需要的多。我相信有可能走得更快。由此,我的意思是不生成总和为>的排列。 ħ
对于给定的H?有更好的算法吗?
答案 0 :(得分:5)
与许多问题一样,当某些术语已知时,解决方案变得更容易找到/研究。
这些问题的解决方案被称为整数组合,它们是整数分区的概括(其中顺序无关紧要,即只回答那些问题)考虑排列是唯一的。)
例如,4的整数分区是:1 + 1 + 1 + 1,1 + 1 + 2,1 + 3,2 + 2,4, 而4的整数成分是:1 + 1 + 1 + 1,1 + 1 + 2,1 + 2 + 1,2 + 1 + 1,1 + 3,3 + 1,2 +2,4。
有一些实现很容易获得(参考语言无关的算法如下):
partitions
package可以为您生成分区。您需要找到每个分区的唯一排列以获得合成(请参阅this SO question)。Compositions
。Compositions
。值得注意的是,这个是使用generators实现的,它可以更有效地使用内存。itertools
来查找排列,然后我需要对唯一排列进行过滤,因此可以通过使用专门用于多重集的排列算法来更有效地完成此操作)。为了更好地理解算法(或自己实施),您可以查看这个不完整但有用的电子书:Combinatorial Generation Frank Ruskey,其中显示了如何在常量摊销时间(CAT)中生成分区。既然你想要合成,你也可以使用CAT算法来生成排列(也在书中),以生成每个整数分区的排列。
Ruskey还解释了如何排名和取消它们,这对于存储/散列结果非常方便。
我相信Knuth的计算机编程艺术第4A卷中也很好地介绍了这些内容,如果您碰巧使用它的话。
ElKamina's suggestion to solve it recursively是一个很好的,但我不会将这种方法用于大H;从R (as well as Python) doesn't optimise tail-calls开始,你最终可能会出现堆栈溢出。
答案 1 :(得分:2)
这是C ++中的implementation
<强> blah.cpp:强>
#include <stdlib.h>
#include <iostream>
#include <vector>
using namespace std;
vector<int> ilist;
void diophantine(int n)
{
size_t i;
if (n==0)
{
for (i=0; i < ilist.size(); i++) cout << " " << ilist[i];
cout << endl;
}
else
{
for (i=n; i > 0; i--)
{
ilist.push_back(i);
diophantine(n-i);
ilist.pop_back();
}
}
}
int main(int argc, char** argv)
{
int n;
if (argc == 2 && (n=strtol(argv[1], NULL, 10)))
{
diophantine(n);
}
else cout << "usage: " << argv[0] << " <Z+>" << endl;
return 0;
}
命令行:
$ g++ -oblah blah.cpp
$ ./blah 4
4
3 1
2 2
2 1 1
1 3
1 2 1
1 1 2
1 1 1 1
$
这是bash
中的implementation:
<强> blah.sh:强>
#!/bin/bash
diophantine()
{
local i
local n=$1
[[ ${n} -eq 0 ]] && echo "${ilist[@]}" ||
{
for ((i = n; i > 0; i--))
do
ilist[${#ilist[@]}]=${i}
diophantine $((n-i))
unset ilist[${#ilist[@]}-1]
done
}
}
RE_POS_INTEGER="^[1-9]+$"
[[ $# -ne 1 || ! $1 =~ $RE_POS_INTEGER ]] && echo "usage: $(basename $0) <Z+>" ||
{
declare -a ilist=
diophantine $1
}
exit 0
这是Python中的implementation
<强> blah.py:强>
#!/usr/bin/python
import time
import sys
def output(l):
if isinstance(l,tuple): map(output,l)
else: print l,
#more boring faster way -----------------------
def diophantine_f(ilist,n):
if n == 0:
output(ilist)
print
else:
for i in xrange(n,0,-1):
diophantine_f((ilist,i), n-i)
#crazy fully recursive way --------------------
def diophantine(ilist,n,i):
if n == 0:
output(ilist)
print
elif i > 0:
diophantine(ilist, n, diophantine((ilist,i), n-i, n-i))
return 0 if len(ilist) == 0 else ilist[-1]-1
##########################
#main
##########################
try:
if len(sys.argv) == 1: x=int(raw_input())
elif len(sys.argv) == 2: x=int(sys.argv[1])
else: raise ValueError
if x < 1: raise ValueError
print "\n"
#diophantine((),x,x)
diophantine_f((),x)
print "\nelapsed: ", time.clock()
except ValueError:
print "usage: ", sys.argv[0], " <Z+>"
exit(1)
答案 2 :(得分:0)
我假设您不是要同时解决方程式。
您可以使用递归或动态编程来解决此问题。
如果您正在使用递归,只需为第一个变量分配一个有效值,并递归地解决其余变量。
这里n是变量的数量,sum是总和。 cursol是部分解决方案(最初设置为[])
def recSolve(n,sum, cursol):
if n==1:
print cursol + [sum]
return
if n == sum:
print cursol + [1 for i in range(n)]
return
else:
for i in range(1, sum-n+2):
recsolve(n-1, sum-i, cursol+[i])
如果你想使用动态编程,你必须记住n和sum的每个组合的解决方案集。