【问题标题】:Solving a Linear Diophantine Equation(see description for examples)求解线性丢番图方程(参见示例说明)
【发布时间】:2011-04-01 12:06:54
【问题描述】:

首先让我澄清一下(在你们解雇我之前),这不是家庭作业问题,我也不是大学生。 :)

编辑 感谢@Klas 和其他人,我的问题现在归结为一个需要以编程方式求解的数学方程。

我正在寻找解决Linear Diophantine Equation 的算法/代码。 对于像我这样的普通人来说,这样的等式如下所示:

示例 1:3x + 4y + 5z = 25(查找 x,y,z 的所有可能值)

示例 2:10p + 5q + 6r + 11s = 224(查找 p,q,r,s 的所有可能值)

示例 3:8p + 9q + 10r + 11s + 12t = 1012(查找 p,q,r,s,t 的所有可能值)

我尝试谷歌搜索无济于事。我原以为已经编写了一些代码来解决这个问题。如果你们遇到了某种已经实现了这个的库,请告诉我。如果解决方案是在 Java 中,没有什么比这更酷了!算法/伪代码也可以。非常感谢。

【问题讨论】:

  • 我很抱歉我的数学术语不好,很久没有这样做了。我正在尝试根据某些限制(这很复杂,其他人不需要知道)随机生成一份试卷。我试图使这个问题尽可能独立和简化。
  • 投票结束;与编程无关。应该在 math.stackexchange.com 之类的东西上
  • 我希望以编程方式解决这个问题。在 Klas 的回答之后,我正在寻找解决丢番图方程的代码。这绝对是与编程相关的恕我直言。

标签: java algorithm math polynomial-math


【解决方案1】:

蛮力递归是一种选择,具体取决于您允许的值或值的数量有多大。

假设:用户输入(被乘数)总是不同的正整数。要找到的系数必须是非负整数。

算法:

Of the multiplicands, let M be the largest.
Calculate C=floor(F/M).
If F=M*C, output solution of the form (0,0,...,C) and decrement C
If M is the only multiplicand, terminate processing
Loop from C down to 0 (call that value i)
  Let F' = F - i*M
  Recursively invoke this algorithm:
    The multiplicands will be the current set minus M
    The goal value will be F'
  For each solution output by the recursive call:
     append i to the coefficient list and output the solution

【讨论】:

【解决方案2】:

这是一道数学题,而不是编程题。一旦有了合适的算法,实施它应该不会太难。

我建议你用谷歌搜索丢番图方程。

我为你找到了explanation

【讨论】:

  • 我认为问题是关于在 Java 中实现解决方案,所以这是一个编程问题。为任意数量的可能系数实现解决方案似乎是一个有趣且相关的编码问题。链接比说“去谷歌搜索”更有用。例如。 mathworld.wolfram.com/DiophantineEquation.html
  • @Steve:这基本上是一道数学题;该问题与 Java 无关(无论使用何种算法,所使用的算法都是相同的)。
  • 来自常见问题解答“如果您的问题通常涵盖……软件算法……那么您来对地方了”。它可能不是特定于 java 的,但 Q. 要求软件算法,所以它是主题。
  • 伙计们,在某些库中是否已经存在解决线性丢番图方程的代码?我尝试谷歌搜索无济于事。
【解决方案3】:

我碰巧为此编写了 Java 代码。请自便。这些解决方案没有经过广泛的测试,但到目前为止似乎效果很好。

package expt.qp;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

public class LinearDiophantine {

    private Map<Integer, Integer> sol = new LinkedHashMap<Integer, Integer>();
    private Map<Integer, Integer> coeff = new HashMap<Integer, Integer>();

    /**
     * @param args
     */
    public static void main(String[] args) {
        // Fill up the data
        // 3x + 4y + 5z + 3a = 25
        LinearDiophantine ld = new LinearDiophantine();
        ld.coeff.put(1, 1);ld.coeff.put(2, 2);ld.coeff.put(3, 3);ld.coeff.put(4, 4);
        Map<Integer, Integer> coeffCopy = new HashMap<Integer, Integer>(ld.coeff);
        int total=30;

        // Real algo begins here
        ld.findPossibleSolutions(total, coeffCopy);

    }

    private void findPossibleSolutions(int total, Map<Integer, Integer> coeff) {
        int index=returnLargestIndex(coeff);
        int range = (int) Math.floor(total/coeff.get(index));
        if(range*coeff.get(index) == total) {
            sol.put(index, range);
            displaySolution();
            //System.out.println();
            range--;
        }
        if(coeff.size() == 1) {
            return;
        }
        while(range>=0) {
            int remTotal = total - range*coeff.get(index);
            Map<Integer, Integer> coeffCopy = new HashMap<Integer, Integer>(coeff);
            coeffCopy.remove(index);
            sol.put(index, range);
            findPossibleSolutions(remTotal, coeffCopy);
            range--;
        }
    }

    private void displaySolution() {
        int total = 0;
        for(int i : sol.keySet()) {
            //System.out.print(coeff.get(i)+"("+sol.get(i)+"), ");
            total = total + (coeff.get(i)*sol.get(i));
        }
        if(total != 30)
            System.out.print(total+",");
    }

    /**
     * @param coeff
     */
    private int returnLargestIndex(Map<Integer, Integer> coeff) {
        int largestKey = coeff.keySet().iterator().next();
        for(int i : coeff.keySet()) {
            if(coeff.get(i)>coeff.get(largestKey)) {
                largestKey=i;
            }
        }
        return largestKey;
    }

}

【讨论】:

  • 解决方案基于@Dave Costa 对问题的(已接受)回答。
  • 仅运行上面的示例似乎显示代码/算法存在一些问题。许多总数 != 30 被打印出来,暗示着某种问题。
  • 是的,我确实对上面的代码做了一些小的改动。不幸的是不记得在哪里,我现在也无法访问该代码。
【解决方案4】:

补充 Klas 非常准确的答案:

希尔伯特的第 10 个问题询问是否存在确定任意丢番图方程是否有解的算法。这种算法确实存在用于求解一阶丢番图方程。然而,Yuri Matiyasevich 在 1970 年证明了获得通解的不可能性

取自:Wolfram MathWorld

【讨论】:

  • 我认为他在谈论非线性丢番图方程。对于线性丢番图方程应该是可能的。
【解决方案5】:

蛮力算法如下(3种可变情况):

int sum = 25;
int a1 = 3;
int a2 = 4;
int a3 = 5;
for (int i = 0; i * a1 <= sum; i++) {
    for (int j = 0; i * a1 + j * a2 <= sum; j++) {
        for (int k = 0; i * a1 + j * a2 + k * a3 <= sum; k++) {
            if (i * a1 + j * a2 + k * a3 == sum) {
                System.out.println(i + "," + j + "," + k);
            }
        }
    }
}

要对 N 变量情况进行推广,您需要转换为递归形式。

这个算法是O(f(size, a)^N) 用于某些函数f

  • 我们可以在f 上设置边界,如下所示:size / MaxValue(a) &lt;= f(size, a) &lt;= size / MinValue(a)
  • 在最坏的情况下(所有a[i]s 都是1f(size, a)size

不管怎样,这对于N 的大值来说是非常可怕的。因此,虽然递归 N 变量算法会更优雅,但可能不太实用。


如果您愿意向 Springer Verlag 支付 34 欧元,这里是 a link to a paper,其中(根据摘要)包括解决 N 变量情况的算法。

【讨论】:

  • 感谢@Stephen 的解决方案,我会尝试看看是否已经存在一些库。如果你找到任何东西,请告诉我。
  • 为什么 i、j 和/或 k 不能为负数?
  • 如果它们可以是负数,则可以有无数种解决方案。 (确实,如果 N > 1 并且存在任何解,则将有无限个解。有一个简单的构造可以证明这一点。)
  • @pavanlimo - 您的计划是否包括对该主题的先前文献的调查、正确性的正式证明、正式的复杂性分析和基准?
【解决方案6】:

要么没有解决方案,要么有无限多的解决方案。通常情况下,您有一个解决方案必须匹配的额外约束。你的问题是这样的吗?

让我们从最简单的情况开始,有两个未知数a*x + b*y = c

第一步是使用Euclidean algorithm找到ab的GCD,我们称之为d。作为奖励,该算法提供x'y',这样a*x' + b*y' = d。如果d 不除c,那么就没有解决办法。否则,解决方案是:

x = x' * (c/d)
y = y' * (c/d)

第二步是找到所有解决方案。这意味着我们必须找到所有pq,例如a*p + b*q = 0。因为如果(x,y)(X, Y) 都是解决方案,那么

a * (X-x) + b * (Y-y) = 0

这个问题的答案是p = b/dq = -a/d,其中d = GCD(a,b) 已经在步骤1 中计算过了。现在的一般解决方案是:

x = x' * (c/d) + n * (b/d)
y = y' * (c/d) - n * (a/d)

其中 n 是一个整数。

第一步很容易扩展到多个变量。我不确定是否要概括第二步。我的第一个猜测是找到所有系数对的解决方案并将这些解决方案组合起来。

【讨论】:

    【解决方案7】:

    这是 Perl 中的一个解决方案。而是使用正则表达式进行破解。

    关注此博客post 使用正则表达式求解代数方程。

    我们可以将以下脚本用于 3x + 2y + 5z = 40

    #!/usr/bin/perl
    $_ = 'o' x 40;
    $a = 'o' x 3;
    $b = 'o' x 2;
    $c = 'o' x 5;
    $_ =~ /^((?:$a)+)((?:$b)+)((?:$c)+)$/;
    print "x = ", length($1)/length($a), "\n";
    print "y = ", length($2)/length($b), "\n";
    print "z = ", length($3)/length($c), "\n";
    

    输出:x=11,y = 1,z = 1

    著名的Oldest plays the piano 谜题最终变成了一个 3 变量方程

    此方法适用于变量实际为正且常数为正的condition

    【讨论】:

    • 这个规模如何?我相信大值会很慢
    【解决方案8】:

    没有库执行此操作的原因类似于您找不到库进行排列的原因 - 您生成的数据太多,这可能是错误的做法。

    更准确地说,如果您有n 变量的总和为X,那么您将有O(X<sup>n-1</sup>) 答案。 Xn 不必太大就会成为问题。

    也就是说,这里有一些 Python 可以相当有效地找出所有必要的信息来编码答案:

    def solve_linear_diophantine (*coeff_tuple):
        coeff = list(coeff_tuple)
        constant = coeff.pop()
    
        cached = []
        for i in range(len(coeff)):
            # Add another cache.
            cached.append({})
    
        def solve_final (i, remaining_constant):
            if remaining_constant in cached[i]:
                return cached[i][remaining_constant]
            answer = []
            if i+1 == len(coeff):
                if 0 == remaining_constant%coeff[i]:
                    answer = [(remaining_constant/coeff[i], [])]
            else:
                for j in range(remaining_constant/coeff[i] + 1):
                    finish = solve_final(i+1, remaining_constant - j*coeff[i])
                    if finish is not None:
                        answer.append((j, finish))
            if 0 == len(answer):
                cached[i][remaining_constant] = None
                return None
            else:
                cached[i][remaining_constant] = answer
                return answer
    
        return solve_final(0, constant)
    

    当我说“编码”时,数据结构看起来像这样。对于每个可能的系数,我们将得到一个 (coefficient, [subanswers]) 数组。只要有可能,它就会重用子答案以使数据结构尽可能小。如果你不能,你可以递归地将其扩展为一系列答案,这样做你会非常有效。 (实际上它是O(nX)。)您只需很少重复逻辑即可一遍又一遍地发现相同的事实。 (但是返回的列表可能会变得非常大,因为要返回的答案列表很大。)

    【讨论】:

      猜你喜欢
      • 2019-03-15
      • 2012-04-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多