Генерация m чисел, которые равны 100, но разница между двумя числами должна быть больше 4 - PullRequest
1 голос
/ 02 июля 2019

Я пытаюсь генерировать числа , которые равны 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--
        }
      }
    }
  }  

И работает отлично. Мой следующий подход был попыткой сравнить нормализованные числа друг с другом через два для циклов и операторов 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 после смены.

Я думал создать другой цикл, который будет работать, пока есть различия. Но я не уверен, что это сработает. Поэтому мне было интересно, есть ли лучшее рабочее решение для этой проблемы - возможно, возможность генерировать эти числа с разницей в размерах от начала и не нужно менять их после.

Ответы [ 4 ]

2 голосов
/ 02 июля 2019
  • Генерация четырех одинаковых случайных чисел из одного диапазона
  • Масштабируйте их так, чтобы они складывались до 76 (100 - разделители, см. Ниже); отрегулируйте случайным образом при необходимости, чтобы учесть остаток
  • Вставить разделители: отсортировать, затем добавить 4 к первому, 8 ко второму, 12 к последнему (без корректировки на ноль).

Пример

  • Генерация [102, 387, 386, 284]
  • Масштаб: [102, 387, 386, 284] * 76 / (102 + 387 + 386 + 284) оценивается как [6, 25, 25, 18]
  • Adjust: это всего 74, поэтому случайным образом добавьте 1 к двум элементам: [6, 25, 26, 19]
  • Сортировка: [6, 19, 25, 26]
  • Вставить разделители: [6, 23, 33, 38]

Добавляет до 100 с гарантией, что они разделены не менее чем на 4, [EDIT] очень мало циклов (чтобы не делить на ноль) и с минимальным произвольным нарушением распределения. Для любопытных вот как это выглядит:

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"/>
0 голосов
/ 02 июля 2019

Вот статистический подход к проблеме, может быть полезно взглянуть на проблему с другой точки зрения. Мы будем использовать полиномиальное распределение , натуральное свойство которого равно сумме всегда , равной n (в вашем случае 100). Можно было бы поиграть с вероятностями таким образом, что разница между выборочными числами, вероятно, будет больше 4. Поскольку средние значения для полинома равны n * p i , мы разносим вероятности далеко друг от друга. После выборки массив сортируется и проверяется на наличие различий. Если они близки, мы отклоняем образец и призываем к повторному розыгрышу. Я использую 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);
0 голосов
/ 02 июля 2019

Небольшой перебор. После запуска возьмите случайный результат из массива.

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; }
0 голосов
/ 02 июля 2019

<!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>

Это простой ответ

...