【问题标题】:All natural numbers which sum up to N and where the inverses sum up to one总和为 N 且倒数为 1 的所有自然数
【发布时间】:2013-05-11 09:38:25
【问题描述】:

我有一个问题要解决。给定N 自然数。我需要找到一个自然数列表,这些自然数的总和等于给定的数字,同时倒数最大为 1。

a + b + c + ... = N
1/a + 1/b + 1/c + ... = 1

abc 不必是唯一的。

我用 Java 编写了以下代码。它适用于简单的情况,但对于 N > 1000 来说已经非常慢了。

我如何重写该方法,使其即使对数百万也能快速运行?也许,我应该放弃递归或用我错过的数学技巧切断一些分支?

SSCE:

private final static double ONE = 1.00000001;

public List<Integer> search (int number) {
    int bound = (int)Math.sqrt(number) + 1;
    List<Integer> list = new ArrayList<Integer>(bound);

    if (number == 1) {
        list.add(1);
        return list;
    }

    for (int i = 2; i <= bound; i++) {
        list.clear();
        if (simulate(number, i, list, 0.0)) break;
    }

    return list;
}


//TODO: how to reuse already calculated results?
private boolean search (int number, int n, List<Integer> list, double sum) {
    if (sum > ONE) {
        return false;
    }

    //would be larger anyway
    double minSum = sum + 1.0 / number;
    if (minSum > ONE) {
        return false;
    }

    if (n == 1) {
        if (minSum < 0.99999999) {
            return false;
        }

        list.add(number);
        return true;
    }

    boolean success = false;
    for (int i = 2; i < number; i++) {
        if (number - i > 0) {
            double tmpSum = sum + 1.0 / i;
            if (tmpSum > ONE) continue;

            list.add(i);
            success = search(number - i, n - 1, list, tmpSum);
            if (!success) {
                list.remove(list.size() - 1);
            }

            if (success) break;
        }
    }

    return success;
}

【问题讨论】:

  • 我认为1/a + 1/b + 1/c + ... = N 是一个错字,应该是= 1,不是吗?
  • 你说逆列表应该总结为 1 (你的公式说 N): a + b + c + ... = N 1 /a + 1/b + 1/c + ... = 1
  • 我认为你应该期望运行时 O(n^2) 的量级。如果您实施智能修剪,您可能会节省一个常量工厂,恕我直言,这不会得到回报。我认为你不能用浮点运算来解决这个问题。优化可能应该去 java 编码。
  • 为了清楚起见,{a, b, ...} 应该是整数,对吧?另外,你允许正数和负数吗?
  • 应该 a, b, c, ... de dinstinct 吗?

标签: java algorithm optimization recursion numbers


【解决方案1】:

论文"A Theorem on Partitions", 1963 by Graham, R. L. 表明,对于 N > 77,有一个解决方案,其中使用的数字是不同的,并提出了一种算法来找到这种分解。

算法如下:

  • 如果 N 小于 333 ,请使用预先计算的表来获取结果。
  • 如果 N 是奇数,则找到 d1, d2, d3, d4, ..., dk 的分解 (N-179)/2,然后 3, 7, 78, 91, 2*d1, 2*d2, 2*d3, ..., 2*dk 是 N 的分解
  • 如果 N 是偶数,则为 (N-2)/2 找到分解 d1, d2, d3, d4, ..., dk,然后 2, 2*d1, 2*d2, 2*d3, ..., 2*dk 是 N 的分解

但是由于您不关心分解中有不同的数字,您可以将预先计算结果的表大小减少到 60,如果 N 是奇数,请为 (N-9)/2 找到分解 d1, d2, d3, d4, ..., dk,然后3, 6, 2*d1, 2*d2, 2*d3, ..., 2*dk 是 N 的分解。

【讨论】:

  • (N-179)/2 这太疯狂了!
  • 既然你不关心 dincstinct 数字,你就不需要这个 179,我已经更新了答案。
  • 60(N-9)/2 来自哪里?
  • 你应该阅读这篇论文。 (N-9)/2 来自这样一个事实,即如果 d1, ..., dk(N-9)/2 的分解,那么 3, 6, 2*d1, ..., 2*dkN 的分解。
  • 60 实际上并不是最小的数字。最小数字是 55。23 没有有效的分解,因此对于数字 55,您不能使用此算法。
【解决方案2】:

首先,更改第二个条件,这样您就不必执行任何浮点运算。将 (1/a+1/b+1/c)=1 更改为 bc+ac+ab = abc。您可以使用 O(k) 除法来计算(提示:先计算右侧)。

其次,整合您的数字。示例:如果您有 a,b,c,a,b 作为输入,则合并欺骗并将其存储为两个 a、两个 b 和一个 c。

第三,有一个基于 DP 的解决方案可以有效地解决第一个问题。您还必须存储所有部分答案。但是,您可以非常有效地存储部分答案。例如。将“x=bc+ac+ab”和“y=abc”存储为部分解。当您将 d 添加到组合中时,您有 xnew = x*d+y 和 ynew=y*d。

如果您使用这三个指针,您的解决方案可能会更有效。

【讨论】:

    【解决方案3】:

    如果数字不必是整数a = b = c = ... = sqrt(N) 是一个解决方案。

    如果允许负数,则找到ab 使得8a+3b+1=N(你可以用Euclide's algorithm 计算它们)然后你想要的列表是: 数字 3(3a 次)、数字 2(2b 次)和数字 1(1-a-b 次)

    【讨论】:

    • 它们必须是整数。不允许否定。
    猜你喜欢
    • 2020-01-09
    • 2011-01-11
    • 2013-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多