【问题标题】:Efficient one pass algorithm for finding a set of 'N' numbers whos sum is equal to 'M'?用于查找总和等于“M”的一组“N”个数字的有效单程算法?
【发布时间】:2018-04-19 16:10:55
【问题描述】:

对于给定的问题陈述,什么必须是有效的算法? (首选java)

找到一个由 N 个数字组成的集合 S,使得它们的总和等于 M。集合 S 中的每个数字必须落在与平均 M/N 的给定偏差“D”之内。

int M= 100;
int N= 10;
int D= 3;

这里 M/N 的平均值 = 10。因此,偏差为 3,N 可以是 {7,8,9,10,11,12,13} 中的数字之一

结果必须和这个集合类似:

11 11 9 8 11 9 11 11 9 10

下面是程序,我到现在为止:

    public class RandomNumberGenerator {

    public static void main(String args[]) {
        Random r = new Random();
        int sum = 100;
        int numbers = 10;
        int deviation = 3;

        int iterator = 0;
        int sumTemp = 0;
        int storeArray[] = new int[numbers];
        int average = Math.round((float) sum / (float) numbers);
        int numberOfAttempts = 0;
        int discardedBecauseGreater = 0;
        System.out.println("Average is " + average);

        while (iterator < numbers) {
            int temp = r.nextInt(average + deviation);
            if (temp > average - deviation) {
                storeArray[iterator] = temp;
                sumTemp += temp;
                iterator++;
            }
            if (iterator == numbers) {
                if (sumTemp == sum) {
                    System.out.println("Got the result " + sumTemp);
                    System.out
                            .println("Number of attempts " + numberOfAttempts);
                    System.out.println("Discarded because of greater "
                            + discardedBecauseGreater);
                    for (int i = 0; i < numbers; i++) {
                        System.out.println(storeArray[i]);
                    }

                    break;
                } else {
                    numberOfAttempts++;
                    sumTemp = 0;
                    iterator = 0;
                }
            }

            if (sumTemp > sum) {
                discardedBecauseGreater++;
                sumTemp = 0;
                iterator = 0;
            }

        }
    }
}

【问题讨论】:

  • 仅供参考geeksforgeeks.org/…
  • 使用当前公式{10,10,10,10,..} 是最简单的解决方案。
  • @JohnColeman 这不是子集总和(我假设这是您所指的 NP 完全问题),因为您可以自己在某个范围内得出数字,而不是选择他们从一个集合中提取出来,尽管这个问题在这个阶段似乎还不够明确,无法回答。

标签: java algorithm random


【解决方案1】:

好的,让我们重新表述问题:示例 N 值 Xi 以便:

  • 总和iN Xi = 1
  • 平均 E[Xi] = 1/N
  • 每个 Xi 均值在 +-d 范围内

如果您能够对此类数字进行采样,则可以将其重新缩放为您想要的任何 M

这里描述的内容看起来像Dirichlet distribution,参数有点大。假设我们使用 Dirichlet 分布,其中所有 αi 等于相同的值a。如下链接:

  • αi = a
  • α0 = N*a
  • E[Xi] = αi0 = a / (Na) = 1/N
  • Var[Xi] ~ αi02 = 1/(N2a)

所以如果a 足够大,方差会非常小。看一下右上图与(7,7,7)案例的链接。对于最终调整,我们可能会使用接受/拒绝 - 如果任何值超过 d 方式,我们将其丢弃并重新采样。

代码(一些 presudocode,实际上,我的 Java 已经生锈了)。对于 Dirichlet 分布的采样,我使用众所周知的方法 https://stats.stackexchange.com/questions/69210/drawing-from-dirichlet-distribution,Java 的 Gamma 分布假设来自 Apache 公共库 https://commons.apache.org/proper/commons-math/javadocs/api-3.1/org/apache/commons/math3/distribution/package-tree.html

GammaDistribution[] initDirichlet(double a, int N) {
    GammaDistribution[] r = new GammaDistribution[N];
    double scale = 1.0;
    for(int k = 0; k != N; ++k)
        r[k] = new GammaDistribution(a, 1.0);
    }
    return r;
}

void sampleDirichlet(double[] r, GammaDistribution[] g, int N) {
    double s = 0.0;
    for (int k = 0; k != N; ++k) {
        double v = g[k].sample();
        r[k] = v;
        s += v;
    }
    s = 1.0 / s;
    for (int k = 0; k != N; ++k) {
        r[k] *= s;
    }
}

void sample(double[] r, GammaDistribution[] g, int N, double d) {
    double mean = 1.0/(double)N;
    outer:
    for( ;; ) {
        sampleDirichlet(r, g, N);
        for (int k = 0; k != N; ++k) {
            if (Math.Abs(r[k] - mean) > d)
                continue outer; // reject and start over
        }
        break; // accept
    }
}

int use() {
    int N = 10;
    double a = 7.0;
    double d = 0.05;
    GammaDistribution[] g = initDirichlet(a, N);

    double[] r = new double[N];

    sample(r, g, N, d);
    sample(r, g, N, d);
    sample(r, g, N, d);
    sample(r, g, N, d);
    sample(r, g, N, d);
    ....
}

【讨论】:

    【解决方案2】:

    我认为这个问题是一个变体/类似于倒水问题

    https://en.wikipedia.org/wiki/Water_pouring_puzzle

    您可以选择 n 杯,每杯 m{i} 容量以倒入所需量的水。

    所以选择“n”个数字和“m”。

    您可以找到解决方案并根据您的用例进行调整。

    这是一种可能的解决方案:

    https://gist.github.com/setrar/77fc1c301266afde9f7f

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-26
      • 2020-12-23
      • 2015-12-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多