Как я могу создать указанное количество случайных значений, которые все равны указанному числу в PHP?
Например, я говорю "10" для суммы значений и "10000" для общей суммы.
Сценарий должен был бы рандомизировать 10 различных чисел, которые все равны до 10000. Не больше, не меньше.
Но это также должно быть динамичным. Например, иногда я мог бы ввести "5" или "6" или даже "99" для количества значений и любое число (до миллиарда или даже больше) в качестве общей суммы.
Как бы я поступил так?
РЕДАКТИРОВАТЬ: я должен также отметить, что все числа должны быть положительными целыми числами
6 ответов
Правильный ответ здесь невероятно прост.
Просто представьте белую линию, скажем, 1000 единиц в длину.
Вы хотите разделить линию на десять частей, используя красные метки.
ОЧЕНЬ ПРОСТО, ВЫБЕРИТЕ ДЕВЯТЬ СЛУЧАЙНЫХ ЧИСЕЛ и нанесите красную отметку на каждой из этих точек.
Это так просто. Вы сделали!
Таким образом, алгоритм имеет вид:
(1) выбрать девять случайных чисел от 0 до 1000 (2) поместить девять чисел, ноль и 1000 в массив (3) отсортировать массив (4), используя вычитание, получить десять "расстояний" между значениями массива
Вы сделали
(Очевидно, что если вы хотите, чтобы в конечном наборе не было нулей, в части (1) просто выберите другое случайное число, если вы столкнулись.)
В идеале, как программисты, мы можем "видеть" такие визуальные алгоритмы в наших головах - стараться визуально мыслить, что бы мы ни делали!
Сноска - для любого непрограммиста, читающего это, просто для ясности, пожалуйста, обратите внимание, что это как "первое, что вы когда-либо узнаете, изучая информатику!" то есть я не получаю никакого кредита на это, я просто набрал ответ, так как я наткнулся на странице. Нет, слава мне!
Просто для записи еще один общий подход (в зависимости от желаемого результата, будь вы имеете дело с действительными или целыми числами и другими ограничениями) также очень "ах, ха!" элегантный. Все, что вы делаете, это: получить 10 случайных чисел. Добавьте их. Удивительно просто, просто: умножьте или разделите их все на некоторое число, чтобы итоговая сумма была желаемой! Это так просто!
Может быть что-то вроде этого:
установить максимальное количество, оставшееся до целевого числа
цикл от 1 до количества значений, которые вы хотите - 1
получить случайное число от 0 до максимальной оставшейся суммы
установить новую максимальную сумму, оставшуюся до старой максимальной суммы, оставшейся минус текущее случайное число
повторить цикл
в итоге вы получите "остаток", поэтому последнее число определяется тем, что осталось, чтобы составить исходный итог.
Я считаю, что ответ, предоставленный @JoeBlow, в значительной степени правильный, но только если желаемая "случайность" требует равномерного распределения. В комментарии к этому ответу @Artefacto сказал следующее:
It may be simple but it does not generate uniformly distributed numbers...
Itis biased in favor of numbers of size 1000/10 (for a sum of 1000 and 10 numbers).
Это вызывает вопрос, который был упомянут ранее относительно желаемого распределения этих чисел. Метод JoeBlow гарантирует, что элемент 1 имеет такую же вероятность быть номером x, что и элемент 2, что означает, что он должен быть смещен в сторону чисел размера Max/n. Вопрос о том, хотел ли ОП более вероятный выстрел по одному элементу, приближающемуся к Максу, или требовал равномерного распределения, не был ясен в этом вопросе. [Извинения - я не уверен с точки зрения терминологии, дает ли это "равномерное распределение", поэтому я ссылаюсь на него только в терминах непрофессионала]
В целом, неверно говорить, что "случайный" список элементов обязательно распределен равномерно. Отсутствующий элемент, как указано в других комментариях выше, является желаемым распределением.
Чтобы продемонстрировать это, я предлагаю следующее решение, которое содержит последовательные случайные числа случайного шаблона распределения. Такое решение было бы полезно, если бы первый элемент имел одинаковый шанс на любое число от 0 до N, а каждое последующее число имеет равный шанс на любое число от 0 до [Оставшееся общее количество]:
[Pseudo code]:
Create Array of size N
Create Integer of size Max
Loop through each element of N Except the last one
N(i) = RandomBetween (0, Max)
Max = Max - N(i)
End Loop
N(N) = Max
Может быть необходимо взять эти элементы и рандомизировать их порядок после того, как они были созданы, в зависимости от того, как они будут использоваться [в противном случае средний размер каждого элемента уменьшается с каждой итерацией].
Генерация 10 случайных чисел до 10000 . Сортируйте их от большого к маленькому: от g0 до g9
g0 = 10000 - r0
g1 = r0 - r1
...
g8 = r8 - r9
g9 = r9
Это даст 10 случайных чисел во всем диапазоне, что в сумме составит 10000 .
Связанный: http://www.mathworks.cn/matlabcentral/newsreader/view_thread/141395
Смотрите этот пакет MATLAB. Он сопровождается файлом с теорией реализации.
Эта функция генерирует случайные, равномерно распределенные векторы, x = [x1,x2,x3,...,xn]', которые имеют указанную сумму s и для которых у нас есть <= xi <= b, для указанных значений a и б. Полезно рассматривать такие векторы как точки, принадлежащие n-мерному евклидову пространству и лежащие в n-1-мерной гиперплоскости, ограниченной суммой s. Поскольку для всех a и b проблема может быть легко изменена на случай, когда a = 0 и b = 1, в дальнейшем мы будем предполагать, что это так, и что мы работаем в единичном n-мерном "куб".
Это реализация (© Roger Stafford):
function [x,v] = randfixedsum(n,m,s,a,b)
% Rescale to a unit cube: 0 <= x(i) <= 1
s = (s-n*a)/(b-a);
% Construct the transition probability table, t.
% t(i,j) will be utilized only in the region where j <= i + 1.
k = max(min(floor(s),n-1),0); % Must have 0 <= k <= n-1
s = max(min(s,k+1),k); % Must have k <= s <= k+1
s1 = s - [k:-1:k-n+1]; % s1 & s2 will never be negative
s2 = [k+n:-1:k+1] - s;
w = zeros(n,n+1); w(1,2) = realmax; % Scale for full 'double' range
t = zeros(n-1,n);
tiny = 2^(-1074); % The smallest positive matlab 'double' no.
for i = 2:n
tmp1 = w(i-1,2:i+1).*s1(1:i)/i;
tmp2 = w(i-1,1:i).*s2(n-i+1:n)/i;
w(i,2:i+1) = tmp1 + tmp2;
tmp3 = w(i,2:i+1) + tiny; % In case tmp1 & tmp2 are both 0,
tmp4 = (s2(n-i+1:n) > s1(1:i)); % then t is 0 on left & 1 on right
t(i-1,1:i) = (tmp2./tmp3).*tmp4 + (1-tmp1./tmp3).*(~tmp4);
end
% Derive the polytope volume v from the appropriate
% element in the bottom row of w.
v = n^(3/2)*(w(n,k+2)/realmax)*(b-a)^(n-1);
% Now compute the matrix x.
x = zeros(n,m);
if m == 0, return, end % If m is zero, quit with x = []
rt = rand(n-1,m); % For random selection of simplex type
rs = rand(n-1,m); % For random location within a simplex
s = repmat(s,1,m);
j = repmat(k+1,1,m); % For indexing in the t table
sm = zeros(1,m); pr = ones(1,m); % Start with sum zero & product 1
for i = n-1:-1:1 % Work backwards in the t table
e = (rt(n-i,:)<=t(i,j)); % Use rt to choose a transition
sx = rs(n-i,:).^(1/i); % Use rs to compute next simplex coord.
sm = sm + (1-sx).*pr.*s/(i+1); % Update sum
pr = sx.*pr; % Update product
x(n-i,:) = sm + pr.*e; % Calculate x using simplex coords.
s = s - e; j = j - e; % Transition adjustment
end
x(n,:) = sm + pr.*s; % Compute the last x
% Randomly permute the order in the columns of x and rescale.
rp = rand(n,m); % Use rp to carry out a matrix 'randperm'
[ig,p] = sort(rp); % The values placed in ig are ignored
x = (b-a)*x(p+repmat([0:n:n*(m-1)],n,1))+a; % Permute & rescale x
return
Обновление: @Joe Blow имеет идеальный ответ. В моем ответе есть особенность генерации кусков примерно одинакового размера (или, по крайней мере, разницы не более (10000 / 10)), и поэтому он остается на месте.
Самый простой и быстрый подход, который приходит мне в голову:
Разделите 10000 на 10 и сохраните значения в массиве. (В 10 раз больше
10000
)Пройдите через каждый из 10 элементов в
for
петля.Из каждого элемента вычтите случайное число между (10000/10).
Добавьте этот номер к следующему элементу.
Это даст вам несколько случайных значений, которые при добавлении приведут к конечному значению (игнорируя проблемы с плавающей запятой).
Должно быть на полпути легко осуществимо.
В какой-то момент вы достигнете максимального целочисленного предела PHP. Не уверен, как далеко это может быть использовано для ценностей в миллиард и более.