繁体   English   中英

将n个双倍数均匀除以总和s,其中每个双倍数均小于1(具有适当的性能)

[英]Uniformly divide `n` doubles with a total sum `s`, where each double is below 1 (with proper performance)

我目前正在努力解决这个(代码高尔夫球)挑战 ,并且在给定整数n和总十进制总和s (考虑到我们应该支持的范围 ,我很难组合这四个要求: 1 <= n <= 100 <= s <= n ):

  1. n项目应全部随机,并均匀划分
  2. 所有项目的总和必须等于s
  3. 所有项目均不得高于1.0
  4. 代码必须为每1秒中运行n <= 10 ,而不管s

基于这个Stackoverflow答案,我知道如何在给定范围内均匀地划分项目。 例如,在n=5, s=4.5 ,我可以创建一个范围为[0, 1.5]n-1 = 4项目的列表,并带有一个额外的前导0和尾随1.5 然后,我可以计算所有差异(因此所有差异的总和将等于s=4.5 )。 这里是一个可能的实现:

int n=5; double s=4.5;

double[] randomValues = new double[n+1];
randomValues[n] = s;
for(int i=1; i<n; i++)
  randomValues[i] = Math.random()*s;
java.util.Arrays.sort(randomValues);
System.out.println("Random values:\n"+java.util.Arrays.toString(randomValues)+"\n");

double[] result = new double[n];
for(int i=0; i<n; i++)
  result[i] = Math.abs(randomValues[i]-randomValues[i+1]);
System.out.println("Result values:\n"+java.util.Arrays.toString(result)+"\n");

double resultSum = java.util.Arrays.stream(result).sum();
System.out.println("Sum: "+resultSum);
System.out.println("Is the result sum correct? "+(s==resultSum?"yes":"no"));

输出示例:

Random values:
[0.0, 1.3019797415889383, 1.9575834386978177, 2.0417721898726313, 4.109698885802333, 4.5]

Result values:
[1.3019797415889383, 0.6556036971088794, 0.08418875117481361, 2.0679266959297014, 0.39030111419766733]

Sum: 4.5
Is the result sum correct? yes

在线尝试。

以上符合上述四个要求中的三个(1、2和4)。 但是,不是3。


我可以在其周围包裹一个循环,只要result -array中的一项仍然大于1,该循环就会继续:

for(boolean flag=true; flag; ){
  ... // Same code as above

  flag=false;
  for(double d:result)
    if(d>1)
      flag=true;
}

对于大多数ns来说,这仍然相对较快:
在线尝试 (删除/移动打印以免控制台溢出)

但是,最后有四个//禁用的测试用例,其预期随机值的平均值接近1 ,花费的时间太长(甚至在TIO上60秒后超时)。

所以这是我的主要问题所在。 如何像我一样均匀地划分值,并牢记每个值的[0, 1]范围,并具有良好的性能? 在顶部的链接代码高尔夫球挑战中,我看到了一些其他编程语言的工作示例,例如Python或JavaScript,但是将较长的代码高尔夫球挑战从一种编程语言移植到另一种编程语言通常不是很简单。

好的,找到了一个基于已经给出的JavaScript答案的解决方案。 如果s*s>n我将s更改为ns并设置一个标志以在结果数组中使用1-diff而不是diff

因此,总计为:

boolean inversedFlag = 2*s>n;
double S = s;
if(inversedFlag)
  S = n-S;

double[] result = new double[n];

for(boolean flag=true; flag; ){
  double[] randomValues = new double[n+1];
  randomValues[n] = S;
  for(int i=1; i<n; i++)
    randomValues[i] = Math.random()*S;
  java.util.Arrays.sort(randomValues);

  for(int i=0; i<n; i++){
    double diff = Math.abs(randomValues[i]-randomValues[i+1]);
    result[i] = inversedFlag ? 1-diff : diff;
  }

  flag=false;
  for(double d:result)
    flag |= d>1;
}

System.out.println("Result values:\n"+java.util.Arrays.toString(result)+"\n");

double resultSum = java.util.Arrays.stream(result).sum();
System.out.println("Sum: "+resultSum );
System.out.println("Is the result sum correct? "+(s==resultSum?"yes":"no"));

System.out.println();
System.out.println("--------------------------------------------------");
System.out.println();

在线尝试。

现在,我只需要再次打高尔夫球,但这与Stackoverflow问题无关。 :)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM