Java 求两个线性等式成立的整数集
我可以使用什么算法来查找Java 求两个线性等式成立的整数集,java,c#,python,algorithm,language-agnostic,Java,C#,Python,Algorithm,Language Agnostic,我可以使用什么算法来查找n1,n2,…,的所有正整数值的集合,n7以下不等式适用 97n1 + 89n2 + 42n3 + 20n4 + 16n5 + 11n6 + 2n7 - 185 > 0 -98n1 - 90n2 - 43n3 - 21n4 - 17n5 - 12n6 - 3n7 + 205 > 0 n1 >= 0, n2 >= 0, n3 >=0. n4 >=0, n5 >=0, n6 >=0, n7 >= 0 例如,一组n1=2,
n1,n2,…,的所有正整数值的集合,n7
以下不等式适用
97n1 + 89n2 + 42n3 + 20n4 + 16n5 + 11n6 + 2n7 - 185 > 0
-98n1 - 90n2 - 43n3 - 21n4 - 17n5 - 12n6 - 3n7 + 205 > 0
n1 >= 0, n2 >= 0, n3 >=0. n4 >=0, n5 >=0, n6 >=0, n7 >= 0
例如,一组n1=2,n2=n3=…=n7=0
使不等式为真。如何找出所有其他值集?类似的问题已经发布在网站上
添加::我需要为n个变量(可能很大)概括解决方案。我可以申请什么程序?对于另一个特殊情况n=8
97n1 + 89n2 + 42n3 + 20n4 + 16n5 + 11n6 + 6n7 + 2n8 - 185 > 0
-98n1 - 90n2 - 43n3 - 21n4 - 17n5 - 12n6 - 7 - 3n8 + 205 > 0
n1 >= 0, n2 >= 0, n3 >=0. n4 >=0, n5 >=0, n6 >=0, n7 >= 0, n8 >= 0
Python需要永远的时间Wolfram Mathematica
揭示了4015
在不到一分钟的时间内就有解决方案
Length[Solve[{97 n1 + 89 n2 + 42 n3 + 20 n4 + 16 n5 + 11 n6 + 6 n7 +
2 n8 - 185 > 0,
-98 n1 - 90 n2 - 43 n3 - 21 n4 - 17 n5 - 12 n6 - 7 n7 - 3 n8 +
205 > 0,
n1 >= 0, n2 >= 0, n3 >= 0, n4 >= 0, n5 >= 0, n6 >= 0, n7 >= 0,
n8 >= 0}, {n1, n2, n3, n4, n5, n6, n7, n8}, Integers]]
有1013个解决方案,但我不知道解决这个问题的最有效方法
看看第二个不等式,
17*n5
不能大于205
(否则整个左手边不能为正)。这导致n5似乎类似于带整数解的线性规划。我认为已经实现了一些算法。
试试看。 < P>在你给的两个例子中,我都注意到了同样的模式。例如,对于第一种情况,如果将两个方程相加,将得到
-n1 - n2 - n3 - n4 - n5 - n6 - n7 + 20 > 0
可以重新排列为
n1 + n2 + n3 + n4 + n5 + n6 + n7 < 20
find_solutions(20)
在0.6秒内找到所有1013个解决方案。类似地,对于第二种情况,它可以在2.3秒内找到所有4015个解决方案。现在,这根本不容易推广,但它表明,使用智能方法,Python或任何其他语言,不必太慢
另一方面,递归允许我们将其推广到任意数量的嵌套循环,但代价是运行速度稍慢
def find_solutions(N, coeffs, depth=0, variables=None, subtotal=None, solutions=None):
if variables is None:
solutions = []
subtotal = [0 for _ in xrange(len(coeffs[0]))]
variables = [0 for _ in xrange(len(coeffs[0])-1)]
if depth == len(coeffs[0])-2:
for v in xrange(N-sum(variables[:depth])):
conditions = all(
subtotal[i]+coeffs[i][depth]*v > coeffs[i][-1]
for i in xrange(len(coeffs))
)
if conditions:
variables[depth] = v
solutions.append(tuple(variables))
else:
for v in xrange(N-sum(variables[:depth])):
variables[depth] = v
total = [subtotal[i]+coeffs[i][depth]*v for i in xrange(len(coeffs))]
find_solutions(N, coeffs, depth+1, variables, total, solutions)
if depth == 0:
return solutions
要运行此操作,请为每个方程生成系数并将其传递给函数。记住常数的符号是倒转的
coeffs = [
[97, 89, 42, 20, 16, 11, 2, 185],
[-98, -90, -43, -21, -17, -12, -3, -205]
]
solutions = find_solutions(20, coeffs)
print(len(solutions))
这一个在1.6秒内完成n=7的情况,在5.8秒内完成n=8的情况。如果你希望你的n变得非常大,我会研究任何可能的优化,但目前看来还是令人满意的
剩下的问题是,你的方程之和是否总是简化为n1+n2+。。。nn
。如果不是这样的话,有一个简单的解决方案,但是我选择了不要过早地将代码过度概括到您提供的示例之外
最后,我设想同样的方法可以在Java或C#中实现,而且可能会更快。如果您的一般情况需要更长的时间来解决,我不介意这样做。Reti43的想法是正确的,但是有一种快速递归解决方案,可以在对不等式限制较少的假设下工作
def solve(smin, smax, coef1, coef2):
"""
Return a list of lists of non-negative integers `n` that satisfy
the inequalities,
sum([coef1[i] * n[i] for i in range(len(coef1)]) > smin
sum([coef2[i] * n[i] for i in range(len(coef1)]) < smax
where coef1 and coef2 are equal-length lists of positive integers.
"""
if smax < 0:
return []
n_max = ((smax-1) // coef2[0])
solutions = []
if len(coef1) > 1:
for n0 in range(n_max + 1):
for solution in solve(smin - n0 * coef1[0],
smax - n0 * coef2[0],
coef1[1:], coef2[1:]):
solutions.append([n0] + solution)
else:
n_min = max(0, (smin // coef1[0]) + 1)
for n0 in range(n_min, n_max + 1):
if n0 * coef1[0] > smin and n0 * coef2[0] < smax:
solutions.append([n0])
return solutions
对于这样的长期问题
smin, coef1 = 185, (97, 89, 42, 20, 16, 11, 2)
smax, coef2 = 205, (98, 90, 43, 21, 17, 12, 3)
solns7 = solve(smin, smax, coef1, coef2)
len(solns7)
1013
smin, coef1 = 185, (97, 89, 42, 20, 16, 11, 6, 2)
smax, coef2 = 205, (98, 90, 43, 21, 17, 12, 7, 3)
solns8 = solve(smin, smax, coef1, coef2)
len(solns8)
4015
solutions = solve(185, 205, (97, 89, 42, 20, 16, 11, 6, 2))
len(solutions)
4015
在我的Macbook上,这两个问题都在几毫秒内得到解决。这应该可以很好地扩展到稍大的问题,但从根本上说,系数N的数量是O(2^N)。它实际扩展的程度取决于附加系数的大小-系数越大(与smax smin相比),可能的解决方案越少,运行得越快
更新:通过对链接的讨论,我发现这两个不等式之间的关系是问题结构的一部分。鉴于此,可以给出一个稍微简单的解决方案。下面的代码还包括一些额外的优化,在我的笔记本电脑上,这些优化将8变量情况下的解决方案从88毫秒加速到34毫秒。我在22个变量的例子中尝试了这个方法,在不到一分钟的时间里得到了结果,但是对于数百个变量来说,它永远都不实用
def solve(smin, smax, coef):
"""
Return a list of lists of non-negative integers `n` that satisfy
the inequalities,
sum([coef[i] * n[i] for i in range(len(coef)]) > smin
sum([(coef[i]+1) * n[i] for i in range(len(coef)]) < smax
where coef is a list of positive integer coefficients, ordered
from highest to lowest.
"""
if smax <= smin:
return []
if smin < 0 and smax <= coef[-1]+1:
return [[0] * len(coef)]
c0 = coef[0]
c1 = c0 + 1
n_max = ((smax-1) // c1)
solutions = []
if len(coef) > 1:
for n0 in range(n_max + 1):
for solution in solve(smin - n0 * c0,
smax - n0 * c1,
coef[1:]):
solutions.append([n0] + solution)
else:
n_min = max(0, (smin // c0) + 1)
for n0 in range(n_min, n_max + 1):
solutions.append([n0])
return solutions
此解决方案直接枚举有界区域中的晶格点。由于您需要所有这些解决方案,因此获得这些解决方案所需的时间最多与绑定晶格点的数量成正比,而绑定晶格点的数量随维度(变量)的数量呈指数增长。(最多13个整数)
以下是在1600个内核上使用gpgpu(opencl)的丑陋蛮力,在9.3毫秒内找到1013(7整数)个解决方案,包括从gpu到cpu内存的阵列下载时间:
编辑:更正了n1、n2、n3,因为它们是1,20400,而不是20,20,20有限
__kernel void solver1(__global __write_only float * subCount){
int threadId=get_global_id(0);
int ctr=0;
int n1=threadId/400;
int n2=(threadId/20)%20;
int n3=threadId%20;
for(int n4=0;n4<=20;n4++)
for(int n5=0;n5<=20;n5++)
for(int n6=0;n6<=20;n6++)
for(int n7=0;n7<=20;n7++)
if (
(97*n1 + 89*n2 + 42*n3 + 20*n4 + 16*n5 + 11*n6 + 2*n7 - 185 > 0) &&
(-98*n1 - 90*n2 - 43*n3 - 21*n4 - 17*n5 - 12*n6 - 3*n7 + 205 > 0))
{ctr++;}
subCount[threadId]=ctr;
}
\uuuuuu内核无效解算器1(\uuuuu全局\uuuuu只写浮点*子计数){
int-threadId=get\u-global\u-id(0);
int ctr=0;
int n1=螺纹ID/400;
int n2=(线程ID/20)%20;
int n3=线程ID%20;
对于(int n4=0;n4伪代码:
for each inequation:
find all real roots of the equivalent equation, i.e. the zero-crossings
for each interval between two adjacent roots:
pick any number strictly inside the interval
evaluate the polynomial in that point
if the evaluated polimonial is positive:
add every integer in the interval to the list of solutions to that inequation
(treat the open-ended intervals outside the extreme roots as special cases, they may contain infinite solutions)
find the integers that are in all the lists of solutions to the individual equations
n
后面的数字是指数吗?这是一个线性不等式吗?使用“c++fortran不等式算法”搜索互联网时有什么结果?你可以通过写下求解不等式所需的步骤来发明自己的算法。找到一组线性不等式是否至少有一个整数解的问题是NP完全问题。“可行解”“整数线性规划”是有帮助的搜索词,但是,正如NP完整性所表明的,你不会得到简单的答案。@ThomasMatthews不,解是线性的。它是关于寻找矩阵方程AX=B的所有特定解,其元素大于零。我将搜索不等式算法并查看。正如我所担心的,解是丑陋的。也许我应该取n’的所有范围的笛卡尔积,一旦你设置了n1,常数就变成205-98n1,所以要探索的n2的边界可以缩小……这就是我在最后提出的观点。还有其他方法可以改进这一点。例如,如果你加上2个原始不等式,你得到的值之和不能超过20。我们可以推广f吗或者n的其他值?例如,我不知道有多少个循环?我知道Wolfram Mathematica中有一个算法。
def solve(smin, smax, coef):
"""
Return a list of lists of non-negative integers `n` that satisfy
the inequalities,
sum([coef[i] * n[i] for i in range(len(coef)]) > smin
sum([(coef[i]+1) * n[i] for i in range(len(coef)]) < smax
where coef is a list of positive integer coefficients, ordered
from highest to lowest.
"""
if smax <= smin:
return []
if smin < 0 and smax <= coef[-1]+1:
return [[0] * len(coef)]
c0 = coef[0]
c1 = c0 + 1
n_max = ((smax-1) // c1)
solutions = []
if len(coef) > 1:
for n0 in range(n_max + 1):
for solution in solve(smin - n0 * c0,
smax - n0 * c1,
coef[1:]):
solutions.append([n0] + solution)
else:
n_min = max(0, (smin // c0) + 1)
for n0 in range(n_min, n_max + 1):
solutions.append([n0])
return solutions
solutions = solve(185, 205, (97, 89, 42, 20, 16, 11, 6, 2))
len(solutions)
4015
__kernel void solver1(__global __write_only float * subCount){
int threadId=get_global_id(0);
int ctr=0;
int n1=threadId/400;
int n2=(threadId/20)%20;
int n3=threadId%20;
for(int n4=0;n4<=20;n4++)
for(int n5=0;n5<=20;n5++)
for(int n6=0;n6<=20;n6++)
for(int n7=0;n7<=20;n7++)
if (
(97*n1 + 89*n2 + 42*n3 + 20*n4 + 16*n5 + 11*n6 + 2*n7 - 185 > 0) &&
(-98*n1 - 90*n2 - 43*n3 - 21*n4 - 17*n5 - 12*n6 - 3*n7 + 205 > 0))
{ctr++;}
subCount[threadId]=ctr;
}
__kernel void solver2(__global __write_only float * subCount){
int threadId=get_global_id(0);
int ctr=0;
int n1=threadId/160000; int c1n1=97*n1; int c2n1=-98*n1;
int n2=(threadId/8000)%20; int c1n2=89*n2; int c2n2=- 90*n2 ;
int n3=(threadId/400)%20; int c1n3=42*n3; int c2n3=- 43*n3 ;
int n4=(threadId/20)%20; int c1n4=20*n4 ;int c2n4=- 21*n4 ;
int n5=threadId%20; int c1n5=16*n5 ;int c2n5=- 17*n5 ;
int t1=c1n1+c1n2+c1n3+c1n4+c1n5;
int t2=c2n1+c2n2+c2n3+c2n4+c2n5;
for(int n6=0;n6<=20;n6++)
for(int n7=0;n7<=20;n7++)
for(int n8=0;n8<=20;n8++)
if(t1+ 11*n6 + 2*n7+6*n8 > 185 && t2 - 12*n6 - 3*n7-7*n8 > -205)
ctr++;
subCount[threadId]=ctr;
}
__kernel void solver3(__global __write_only float * subCount){
int threadId=get_global_id(0);
int ctr=0;
int n1=threadId/160000; int c1n1=97*n1; int c2n1=-98*n1;
int n2=(threadId/8000)%20; int c1n2=89*n2; int c2n2=- 90*n2 ;
int n3=(threadId/400)%20; int c1n3=42*n3; int c2n3=- 43*n3 ;
int n4=(threadId/20)%20; int c1n4=20*n4 ;int c2n4=- 21*n4 ;
int n5=threadId%20; int c1n5=16*n5 ;int c2n5=- 17*n5 ;
int t1=c1n1+c1n2+c1n3+c1n4+c1n5;
int t2=c2n1+c2n2+c2n3+c2n4+c2n5;
int m=max( max( max( max(n1,n2),n3),n4),n5);
for(int n6=0;n6<=20-m;n6++)
for(int n7=0;n7<=20-m-n6;n7++)
for(int n8=0;n8<=20-m-n6-n7;n8++)
for(int n9=0;n9<=20-m-n6-n7-n8;n9++)
if(t1+ 11*n6 + 2*n7+6*n8 +3*n9> 185 && t2 - 12*n6 - 3*n7-7*n8-4*n9 > -205)
ctr++;
subCount[threadId]=ctr;
}
for each inequation:
find all real roots of the equivalent equation, i.e. the zero-crossings
for each interval between two adjacent roots:
pick any number strictly inside the interval
evaluate the polynomial in that point
if the evaluated polimonial is positive:
add every integer in the interval to the list of solutions to that inequation
(treat the open-ended intervals outside the extreme roots as special cases, they may contain infinite solutions)
find the integers that are in all the lists of solutions to the individual equations