【问题标题】:Generating m numbers that equal 100 but difference between two numbers have to be greater than 4生成 m 个等于 100 但两个数字之间的差必须大于 4 的数字
【发布时间】:2019-11-12 23:36:45
【问题描述】:

我正在尝试生成等于 100(%) 的数字,但所有数字必须彼此之间至少有 4 个数字。因此,如果我生成 4 个数字,它们必须像这样,例如 [22,28,15,35] 并且不能像这样 [22,28,20,30] 因为 28-30 和 22 - 20 之间的差小于 4。

我能够将生成数字的方法放在一起,看起来像这样。

generate (max, many) {
    this.numbers_array = []
    this.normalized_numbers_array = []
    this.sum = 0 
    for (let i = 0; i < many; i++) {
      this.numbers_array.push(Math.random())
      this.sum += this.numbers_array[i]  
    }
    this.remaining = max
    for (let i = 0; i < this.numbers_array.length; i++) {
      this.outcome= (this.numbers_array[i] / this.sum) * max
      this.normalized_numbers_array[i] = Math.floor(this.outcome)
      this.remaining -= this.normalized_numbers_array[i]
      if (i + 1 == this.numbers_array.length) {
        while (this.remaining > 0) {
          this.normalized_numbers_array[i]++
          this.remaining--
        }
      }
    }
  }  

并且工作正常。我的下一个方法是尝试通过两个 for 循环和 if 语句来比较规范化的数字。然后根据彼此之间的数字之间的差异,我希望 将 2% 加到一个中,并从正在比较的两个数字中的另一个中减去 2% 。然后我将它们放入我想再次标准化的新数组中。

   for (let i = 0; i < this.normalized_numbers_array.length; i++) {

  for (let j = i + 1; j < this.normalized_numbers_array.length; j++) {

    if (Math.abs(this.normalized_numbers_array[i] - this.normalized_numbers_array[j]) < 5) {
        //do something
      if (this.normalized_numbers_array[i] > this.normalized_numbers_array[j]) {
        //do something
      } else if (this.normalized_numbers_array[i] <= this.normalized_numbers_array[j]) {
    //do something  
      }
    }
}

}

不过,这种方法是有缺陷的。通过添加或子结构,我可以产生小于 4 的新差异。例如 [35,18,26,21]->[35,16,26,23] 26 和 23 之间的差异更改后为 3。

我的想法是创建另一个循环,只要存在差异,它就会一直运行。但我不确定这会奏效。所以我想知道这个问题是否有更好的工作解决方案 - 也许能够从一开始就生成那些大小不同的数字,而不必在之后更改它们。

【问题讨论】:

  • 你的区间是从 1 到 100 吗? [1, 100]?
  • 不,也可以是0。

标签: javascript oop random numbers


【解决方案1】:
  • 从同一范围内生成四个均匀随机数
  • 缩放它们,使它们加起来为 76(100 - 分隔符,见下文);根据需要随机调整以解决剩余部分
  • 插入分隔符:排序,然后将 4 加到第一个,8 加到第二个,12 加到最后一个(不调整到第零)。

例子

  • 生成[102, 387, 386, 284]
  • 比例:[102, 387, 386, 284] * 76 / (102 + 387 + 386 + 284) 计算结果为 [6, 25, 25, 18]
  • 调整:只有74,所以随机添加1到两个元素:[6, 25, 26, 19]
  • 排序:[6, 19, 25, 26]
  • 插入分隔符:[6, 23, 33, 38]

加起来为 100,保证至少相隔 4,[编辑] 循环很少(以确保不被零除),并且对分布的任意干扰也很少。对于好奇的人,它的外观如下:

function f() {
  let q, s;
  while (!s) {
    q = Array.from({length: 4}, () => Math.random()); 
    s = q.reduce((a, e) => a + e);
  }
  q.forEach((e, i, q) => q[i] = (e * 76 / s)|0);
  s = q.reduce((a, e) => a + e);
  while (s < 76) {
    q[(Math.random() * 4)|0]++; s++;
  }
  q.sort((a, b) => a - b);
  q.forEach((e, i, q) => q[i] += i * 4);
  return q;
}


const N = 100000;
function draw() {
  const labels = ["#0", "#1", "#2", "#3", "Any"];
  const colours = ["red", "orange", "green", "blue", "black"];
  const data = Array.from({length:5}, (e, i) => ({
    label: labels[i],
    borderColor: colours[i],
    backgroundColor: i == 4 ? "rgba(0, 0, 0, 0.1)" : "rgba(0, 0, 0, 0)",
    pointRadius: 0,
    data: Array.from({length:100}, (e, i) => ({ x: i, y: 0 }))
  }));
  for (let s = 0; s < N; s++) {
    const q = f();
    q.forEach((e, i) => {
      data[i].data[e].y++;
      data[4].data[e].y++;
    });
  }

  const ctx = document.querySelector('canvas').getContext('2d');
  const myChart = new Chart(ctx, {
    type: 'line',
    data: {
        datasets: data
    },
    options: {
      maintainAspectRatio: false,
      animation: false,
      legend: {
        position: 'right'
      },
      scales: {
        yAxes: [{
          ticks: {
            beginAtZero: true
          }
        }],
        xAxes: [{
          type: 'linear',
          ticks: {
            beginAtZero: true,
            max: 100
          }
        }]
      }
    }
  });
};

draw();
document.querySelector('canvas').addEventListener('dblclick', draw);
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.8.0/dist/Chart.min.js"></script>
<canvas width="400" height="180"/>

【讨论】:

  • '并且尽可能均匀随机'这是不可能的。好奇最后会是什么分布。
  • @SeverinPappadeux:是的,我说的很糟糕。我添加了代码和分布图。
  • 酷。你能用你的可视化从我的答案中运行代码吗?只是好奇,多项式将如何表现(您可以放弃拒绝,这非常罕见)。带有概率 [.1, .2, .3, .4] 的多项式将与您的比较好
  • @SeverinPappadeux:随意将其复制粘贴到您的答案中,并用您的代码替换 function f
【解决方案2】:

<!DOCTYPE html>
<html>
<head>

</head>
<style>
.HideX{
	display:none;
};
</style>
<body>
<input type="text" id="QQ">
<input type="text" id="QQ2">
<script>
function isEmpty(map) {
   for(var key in map) {
     if (map.hasOwnProperty(key)) {
        return false;
     }
   }
   return true;
}
function GetRandom(St,Range){
	return Math.floor((Math.random() * Range) + St);
}
function Pick4(){
	var BigPool={};
	var ResultX=[];
  //Generate [1,100]
	for(var i=1;i<=100;i++){
		BigPool[i.toString()]=i;
	}
	
	var Remain=100;
	var Last=100;
	for(var j=0;j<3;j++){
		if(isEmpty(BigPool)){//Althoght impossible but add this exception
			return Pick4();
		}
		var Pick=GetRandom(1,Remain);
		if(BigPool.hasOwnProperty(Pick.toString())){
			delete BigPool[Pick.toString()];//Remove Pick
			ResultX.push(Pick);
			Remain-=Pick;
			for(var i=Remain+1;i<=Last;i++){
				if(BigPool.hasOwnProperty(i.toString())){
					delete BigPool[i.toString()];//Remove above remain
				}
			}
      Last=Remain;
		}else{
			j--;
      continue;
		}
		for(var i=-3;i<=3;i++){//Remove [Pick-3,Pick+3]
			
			if(BigPool.hasOwnProperty((Pick+i).toString())){
				delete BigPool[(Pick+i).toString()];//Remove [Pick-3,Pick+3]
			}
			
		}
	}
	
	if(BigPool.hasOwnProperty(Remain.toString())){
		ResultX.push(Remain);
	}else{
		return Pick4();
	}
	
	
	
	
	return ResultX;
}
var G=Pick4();
document.getElementById("QQ").value = G;
document.getElementById("QQ2").value = G.reduce((a, b) => a + b, 0);
</script>
</body>
</html>

这个答案很简单

【讨论】:

  • 可能很简单,但不正确,因为您生成的数字并不总是至少相隔 4 个数字。
【解决方案3】:

一个小的蛮力方法。运行后,从数组中随机取一个结果。

function get4() {

    function iter(temp, sum) {
        var i, s, t;

        for (i = 0; i <= max; i++) {
            s = sum + i;
            if (s > max) return;
            t = temp.concat(i);
            if (t.length === 4) {
                if (s === max) result.push(t.map((v, i) => v + 4 * i));
                continue;
            }
            iter(t, s);
        }
    }

    var result = [],
        max = 76;

    iter([], 0);
    return result;
}

var result = get4();

console.log(result.length);
console.log(result.map(a => a.join(' ')));
.as-console-wrapper { max-height: 100% !important; top: 0; }

【讨论】:

    【解决方案4】:

    这是解决问题的统计数据驱动方法,可能有助于从不同的角度看待问题。我们将使用 Multinomial distribution,它的自然属性 sum always 等于 n(在您的情况下为 100)。人们可以通过这种方式来玩概率,采样数之间的差异很可能大于 4。因为多项式的平均值是 n*pi,所以我们将概率分开很远。在对采样数组进行排序并检查差异之后。如果他们很接近,我们拒绝样品并要求再次平局。我使用https://github.com/jacobmenick/sampling 代码进行多项采样,还有其他库。

    代码,节点 12.1,Windows 10 x64

    var multinom = SJS.Multinomial(100, [.02, .17, .33, .48]); // far away spacing of means n*p_i
    
    q = multinom.draw().sort((a, b) => a - b); // sorted sample from multinomial
    m = [] // array of pairwise differences, with first element removed
    for (var k = 0; k < q.length - 1; ++k) {
        var curr = q[k];
        var next = q[k + 1];
        m.push(next - curr);
    }
    reject = m.some(el => el < 4); // check for pairwise distance, if true reject sample and get another one
    s = q.reduce((a, b) => a + b, 0); // check for sum, should be always 100
    console.log(q);
    console.log(m);
    console.log(reject);
    console.log(s);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-07-07
      • 1970-01-01
      • 1970-01-01
      • 2014-12-05
      • 1970-01-01
      • 2021-09-10
      • 2014-02-20
      相关资源
      最近更新 更多