Помогите написать числовую функцию
Здравствуйте господа программисты!
Помогите пожалуйста. Необходимо написать функцию деления числа "A" на несколько случайных, сумма которых в итоге будет числом "A". Функция должна иметь 3 параметра, первый это число "A", второе - на сколько частей нужно разбить первое и третье - минимальное значение каждого из чисел. Например: A = 130 получаем 3 случайных числа, кадое из которых больше 20 57, 32, 41 - сумма равна первому число Если идея понятна выслушаю предложения и идеи, как это можно реализовать, если кому будет не сложно приму готовое решение. Заранее благодарен за помощь! |
function funk(a, b, c){ var part = a/b; var q = 0 for (i=0; i<b-1; i++){ var number = [] number[i] = Math.floor(Math.random()*part) while(number[i] <= c){ number[i] = Math.floor(Math.random()*part) } q= q+number[i] var lastnam = a - q alert(number[i]) } alert(lastnam) } Можно так, но это не совсем честный вариант, так как последнее число не случайное. |
PashPP,
предложу свой вариант (последние число почти случайное) function Num(num, kol, min){ if ((kol * min) > num){ alert("Некорректно введены данные"); return; } var kol2 = 0; var ar = []; var obh = num - kol * min; for (var i = 0; i < kol; i++){ ar[i] = min + Math.round(Math.random() * obh); kol2 += ar[i]; } if (kol2 > num){ Num(num, kol, min); return; } if (kol2 < num) ar[i - 1] += num - kol2; alert(ar); } Num(130, 3, 20) |
ashPP, спасибо, вариант интересный, но много в нем не понятного и последнее число всегда больше предыдущих.
|
в идеале все должно быть так, но на деле часто виснет
function funk(a, b, c){ var part = a-((b-1)*c); var q = 0 for (i=1; i<b; i++){ var number = [] number[i] = Math.floor(Math.random()*part) while(number[i] <= c){ number[i] = Math.floor(Math.random()*part) } q= q+number[i] part = a - q - (b-i-1)*c var lastnam = a - q alert(number[i]) } alert(lastnam) } funk(90, 3, 5) Высчитываем первоначальное максимальное значение числа a-((b-1)*c). Где а- число, б - количество частей. с - мин. значение части = part. По ходу выпадания чисел part корректируется в зависимости от выпавших перед ним значений part = a - q - (b-i-1)*c . Где q - cумма всех предыдущих выпаданий чисел. Мне просто интересно, насколько верен мой подход с программной точки зрения, не разбираюсь еще в этих материях. |
Dim@,
var obh = num - kol * min; должно же быть var obh = num - (kol -1)* min; Не? |
Dim@,
А, нет, извини. Эт оя не понял, что ты делал. |
Всем спасибо за помощь, предпоследний вариант переделал под свой лад, все работает на ура!
|
Вот моё решение, в котором все возможности выпадают с одинаковой вероятностью.
function divide(a, n, m) { if (n*m>a) return; var b=[], d=[], p=a-(m-1)*n-1; for (var i=0; i<p; i++) b[i]=i; // делаем массив b из чисел 0, 1, ..., p-1 for (; i>a-m*n; i--) { var c=Math.floor(Math.random()*i); // выбираем случайный элемент из b d.push(b[c]); b[c]=b[i-1]; // удаляем его, считаем, что длина массива b теперь i-1 } d.sort(function (x, y) { return x-y; }); d.push(p); // после этой инструкции массив d будет содержать n различных неотрицательных чисел, отсортированных по возрастанию, последнее из которых p. С помощью этого массива формируется выходной массив, для которого будем использовать переменную b заново b.length=0; b[0]=d[0]+m; for (i=0; i<n-1; i++) b.push(d[i+1]-d[i]+m-1); return b; } alert(divide(130, 3, 20)); Ред. Упс, где-то ошибка, выпало 70, -22, 82. Сейчас попробую исправить. Ред. Уже исправил. Оказывается, метод sort, если не задана сравнительная функция, сортирует элементы как строки. |
Прикольная задачка. Предыдущие ответы не читал, ибо лень. :p
// предполагается, что все аргументы - целые неотрицательные числа function SplitNum (sum, summands, min) { if (summands < 1 || summands * min > sum) { return []; // если операцию выполнить невозможно, возвращаем пустой массив } var i; var range = sum - summands * min; // сумма слагаемых после вычета минимумов var points = []; // здесь будут точки деления диапазона [0; range] for (i=0; i<summands-1; i++) { // произвольных точек деления на 1 меньше, чем слагаемых // генерируем псевдослучайное целое число в диапазоне [0; range] points[i] = Math.floor(Math.random() * (range + 1)); } points.push(0, range); // добавляем стартовую и конечную точки деления points.sort(function (a, b) { return a - b; }); // сортируем по возрастанию var result = []; // сюда будем сгребать слагаемые for (i=0; i<summands; i++) { // слагаемое вычисляем по разнице между точками деления с добавлением минимума result[i] = points[i+1] - points[i] + min; } return result; // возвращаем массив слагаемых } var text = ""; text += SplitNum(130, 3, 20) + "\n"; text += SplitNum(130, 3, 0) + "\n"; text += SplitNum(130, 1, 20) + "\n"; text += SplitNum(100, 10, 10) + "\n"; text += SplitNum(100, 4, 24) + "\n"; text += SplitNum(100500, 10, 10000); alert(text); UPD: oneguy опередил меня, потому что комменты зажопил.)) Хотя наши решения немножко отличаются. :) |
oneguy,
иш какой а :D куда ни глянь ты придешь скажешь есть такие то спецификации и такой-то метод, плюс какие то странные примеры делаешь (я про данный), может быть тебе в интел идти работать - там как раз для случайных чисел делают генератор:lol: |
oneguy,
делай пожалуйста с комментариями код, а то не понятно ничего:) |
oneguy,
Дзен-трансгуманист, ошибся melky всё ещё все думают по разному и у всех все разное :) Quo ire sunt? |
Цитата:
Цитата:
|
у всех сделано через Math.random... кто сможет сделать это через композиции ?)
Цитата:
|
Цитата:
|
мон через % и текущий тайм - но задачка с ограниченой инварьятностью целочисленных решений... простое число делицо ток само на себя, что сбивает весь рандом
|
Цитата:
|
Цитата:
|
Цитата:
Цитата:
function SplitNum (sum, summands, min) { if (summands < 1 || summands * min > sum) { return []; } var i, range = sum - summands * min, points = [], result = []; for (i=0; i<summands-1; i++) { points[i] = Math.floor(Math.random() * (range + 1)); } points.push(0, range); points.sort(function (a, b) { return a - b; }); for (i=0; i<summands; i++) { result[i] = points[i+1] - points[i] + min; } return result; } var counter = [0, 0, 0]; for (var i=0; i<10000; i++) { counter[SplitNum(1, 3, 0).indexOf(1)]++; } alert(counter); OH, SHI~ :D |
Цитата:
|
oneguy,
Я понял, у меня вся фигня в том, что: points[0, 0] приводит к result[0, 0, 1] (вероятность 25%)points[1, 1] приводит к result[1, 0, 0] (вероятность 25%)points[0, 1] и points[1, 0] одинаково приводят к result[0, 1, 0] (вероятность 50%)function SplitNum (sum, summands, min) { if (summands < 1 || summands * min > sum) { return []; } var i, range = sum - summands * min, points = [], result = []; for (i=0; i<summands-1; i++) { points[i] = Math.floor(Math.random() * (range + 1)); } points.push(0, range); points.sort(function (a, b) { return a - b; }); for (i=0; i<summands; i++) { result[i] = points[i+1] - points[i] + min; } return result; } function testDistribution (range, iterations) { var i, counter = []; for (i=0; i<range; i++) { counter[i] = 0; } for (i=0; i<iterations; i++) { counter[SplitNum(1, range, 0).indexOf(1)]++; } return counter; } alert(testDistribution(10, 10000));В идеале распределение должно быть линейным, то есть все числа должны быть близки к 1000, но здесь явно имеем отклонение, близкое к среднеквадратическому. Тогда просто перемешаем результат перед возвратом: function SplitNum (sum, summands, min) { if (summands < 1 || summands * min > sum) { return []; } var i, range = sum - summands * min, points = [], result = []; for (i=0; i<summands-1; i++) { points[i] = Math.floor(Math.random() * (range + 1)); } points.push(0, range); points.sort(function (a, b) { return a - b; }); for (i=0; i<summands; i++) { result[i] = points[i+1] - points[i] + min; } for (i=0; i<summands-1; i++) { // перемешиваем порядок слагаемых var j = Math.floor(Math.random() * (summands - i)) + i; // генерируем j в диапазоне [i; summands-1] var swap = result[i]; result[i] = result[j]; result[j] = swap; // меняем местами значения по индексам i и j } return result; } function testDistribution (range, iterations) { var i, counter = []; for (i=0; i<range; i++) { counter[i] = 0; } for (i=0; i<iterations; i++) { counter[SplitNum(1, range, 0).indexOf(1)]++; } return counter; } alert(testDistribution(10, 10000));Теперь всё в порядке. Aetae, Думаю, что oneguy все-таки прав, потому что если при значительном количестве генераций будет наблюдаться нарушение закона больших чисел, то в зависимости от прикладной задачи это может быть не очень-то и хорошо. :) |
Цитата:
Цитата:
function SplitNum (sum, summands, min) { if (summands < 1 || summands * min > sum) { return []; } var i, range = sum - summands * min, points = [], result = []; for (i=0; i<summands-1; i++) { points[i] = Math.floor(Math.random() * (range + 1)); } points.push(0, range); points.sort(function (a, b) { return a - b; }); for (i=0; i<summands; i++) { result[i] = points[i+1] - points[i] + min; } for (i=0; i<summands-1; i++) { // перемешиваем порядок слагаемых var j = Math.floor(Math.random() * (summands - i)) + i; // генерируем j в диапазоне [i; summands-1] var swap = result[i]; result[i] = result[j]; result[j] = swap; // меняем местами значения по индексам i и j } return result; } function testDistribution (iterations) { var i, counter = []; for (i=0; i<6; i++) { counter[i] = 0; } for (i=0; i<iterations; i++) { var r=SplitNum(2, 3, 0); counter[r.indexOf(2)==-1?r.indexOf(0)+3:r.indexOf(2)]++; } return counter; } alert(testDistribution(27000)); Есть 6 вариантов: [0, 0, 2], [0, 2, 0], [2, 0, 0], [0, 1, 1], [1, 0, 1], [1, 1, 0]. Первые 3 выпадают с вероятностью 4/27, остальные - 5/27. |
Цитата:
|
Цитата:
0002 x1 : 002 0012 x2 : 011 0022 x2 : 002 0112 x1 : 011 0122 x2 : 011 0222 x1 : 002 002 : 1+2+1 = 4x 011 : 2+1+2 = 5x Да, всё верно. Это естественное распределение, так и должно быть. function divide(a, n, m) { if (n*m>a) return; var b=[], d=[], p=a-(m-1)*n-1; for (var i=0; i<p; i++) b[i]=i; for (; i>a-m*n; i--) { var c=Math.floor(Math.random()*i); d.push(b[c]); b[c]=b[i-1]; } d.sort(function (x, y) { return x-y; }); d.push(p); b.length=0; b[0]=d[0]+m; for (i=0; i<n-1; i++) b.push(d[i+1]-d[i]+m-1); return b; } var time1 = (new Date()).getTime(); divide(10000000, 3, 0); var time2 = (new Date()).getTime(); alert(((time2-time1)/1000).toFixed(3) + " sec.");Ой, даааа... Не завидую тому, кто миллиард туда влепит... :lol: Так что ваше решение тоже пока не идеально. ;) |
Длинноник, не вижу рандома вообще.
function RandomSplit( A, parts, min ) { var numbers = []; for ( var i = 0; i < parts; i++ ) { var part = ( A / (parts - i) - min) * Math.random() + min; part = Math.round( part ); numbers[i] = part; A = A - part; } for ( var i = 0; i < numbers.length; i++ ) numbers[i] += A / parts; return numbers; } var counter = [0, 0, 0]; for (var i=0; i<10000; i++) { counter[RandomSplit(1, 3, 0).indexOf(1)]++; } alert(counter); |
Цитата:
|
oneguy,
Цитата:
melky, Цитата:
Цитата:
|
Цитата:
Рассуджая таким же образом, можно было оправдать в SplitNum(1, 3, 0) распределение (1/4, 1/2, 1/4), хотя должно быть (1/3, 1/3, 1/3). |
Я написал ещё один алгоритм для первой части функции - составления массива d. Первый алгоритм больше подходит для больших n/p, второй - для маленьких. В функцию добавился 4-ый необязательный параметр - номер алгоритма. Если он отсутствует - алгоритм выбирается автоматически.
function divide(a, n, m, alg) { if (n*m>a) return; var b=[], d=[], p=a-(m-1)*n-1; if (alg==1||alg!=2&&20*n>p) { // алгоритм 1 for (var i=0; i<p; i++) b[i]=i; // делаем массив b из чисел 0, 1, ..., p-1 while (i>p-n+1) { var c=Math.floor(Math.random()*i); // выбираем случайный элемент из b d.push(b[c]); b[c]=b[--i]; // удаляем его, считаем, что длина массива b теперь i } d.sort(function (x, y) { return x-y; }); } else { // алгоритм 2 for (i=0; i<n-1; i++) { c=Math.floor(Math.random()*(p-i)); var x=0, y=i; while (x<y) { var t=Math.floor((x+y)/2); if (d[t]-t>c) y=t; else x=t+1; } d.splice(x, 0, c+x); } } // конец алгоритма d.push(p); // после этой инструкции массив d будет содержать n различных неотрицательных чисел, отсортированных по возрастанию, последнее из которых p. С помощью этого массива формируется выходной массив, для которого будем использовать переменную b заново b.length=0; b[0]=d[0]+m; for (i=0; i<n-1; i++) b.push(d[i+1]-d[i]+m-1); return b; } //тесты alert(divide(130, 3, 20)+"\n"+ divide(100, 4, 25)+"\n"+ //divide(2000, 1000, 0)+"\n"+ //divide(100500, 100000, 0)+"\n"+ divide(1000000000, 3, 0)+"\n"); |
PashPP, а прокомментируйте пожалуйста вот эту часть кода
13 if (kol2 > num){ 14 Num(num, kol, min); 15 return; 16 } |
leny,
если при создании случайных чисел их сумма больше максимального то функция заново выполняется с начальными аргументами и первая функция останавливается for (var i = 0; i < kol; i++){ ar[i] = min + Math.round(Math.random() * obh); kol2 += ar[i]; // сумма всех псевдо-случайных чисел } if (kol2 > num){ //если сумма всех псевдо-случайных чисел Num(num, kol, min);//функция вызывается с данными аргументами return;// а так как в данной уже нет надобности её останавливаем } |
Цитата:
Может вы знаете где ошибка? |
leny,
проблема в том что происходит зависание из-за частого срабатывания if-a - слишком мала вероятность правильного выпадания псевдо-случайных чисел if (kol2 > num){ //если сумма всех псевдо-случайных чисел Num(num, kol, min);//функция вызывается с данными аргументами return;// а так как в данной уже нет надобности её останавливаем } |
Цитата:
|
Цитата:
|
leny,
действительно - лучше использовать метод oneguy - плюсы +более рандомный +обходит данное препятствие |
Обновил 2-ой алгоритм. Теперь число попадает с первого раза, его не приходится перегенерировать, что делает этот алгоритм быстрее и стабильнее.
|
Цитата:
|
Maxmaxmахimus,
по идее число комбинаций целочисленных делителей ограничено В идеале один раз рандомно выбирать из этого массива всех комбинаций |
Часовой пояс GMT +3, время: 15:35. |