Rand5 () до Rand7 () [закрито]


29

Вам надана функція Rand5 (). Ця функція повертає цілком випадкові цілі числа (однаковий розподіл) між 1 і 5.

Надайте функцію Rand7 (), яка використовує Rand5 () для отримання ідеально випадкових цілих чисел між 1 і 7.



8
Обов’язковий xkcd: xkcd.com/221
Стівен Румбальський

1 і 5 включно? тобто з множини {1,2,3,4,5}?
Аарон Макдейд

1
За якими критеріями визначається один переможець?
kojiro

Той момент, коли ти усвідомлюєш це - насправді старе питання.
nyuszika7h

Відповіді:


11

Ява - 61 символ

int rand7(){int s=0,c=7;while(c-->0)s+=rand5();return s%7+1;}

Тестовий драйвер для перевірки:

class Rand {

    public static void main(String[] args) {
        int[] nums = new int[7];
        // get a lot of numbers
        for(int i = 0; i < 10000000; i++) nums[rand7()-1]++;
        // print the results
        for(int i = 0; i < 7; i++) System.out.println((i+1) + ": " + nums[i]);
    }

    // just for rand5()
    static java.util.Random r = new java.util.Random();

    static int rand5() {
        return r.nextInt(5)+1; // Random.nextInt(n) returns 0..n-1, so add 1
    }

    static int rand7(){int s=0,c=7;while(c-->0)s+=rand5();return s%7+1;}

}

Результати

C:\Documents and Settings\glowcoder\My Documents>java Rand
1: 1429828
2: 1429347
3: 1428328
4: 1426486
5: 1426784
6: 1429853
7: 1429374

C:\Documents and Settings\glowcoder\My Documents>

10
Додаткові бали за "йде оператору"
Стів П

поголити чару? int rand7 () {for (int s = 0, c = 7; c -> 0; s + = rand5 ()); повернути s% 7 + 1;}
Ron

3
Ця відповідь не вірна: ймовірності повернення значень цієї функції у значення від 1 до 7 становлять 0,1430656, 0,1430016, 0,1428224, 0,1426432, 0,1426432, 0,1428224 та 0,1430016 відповідно. Так, різниця між мінімальною та максимальною ймовірностями менша, ніж 0,0005, але все ж у питанні вказано "ідеально випадкові цілі числа".
Ільмарі Каронен

@ilmari Ти маєш рацію - я просто провів тест, і, здається, розподіл не є рівним ... дозволь мені подумати над цим ...
corsiKa

1
@userunknown: Так, ймовірності, які я розміщував, насправді не є наближеннями, вони точні (0,1430656 = 11177/78125 тощо), припускаючи абсолютно випадкові rand5. Я обчислював їх у Maple, використовуючи просту матричну алгебру, але ви можете зробити це олівцем і папером за кілька хвилин, якщо хочете. У будь-якому випадку, виявляється, Омар вже розмістив ті ж цифри (коефіцієнт нормування коефіцієнта) в коментарі до іншої відповіді на пару днів раніше. (Також ps., Ви можете лише попередити одного користувача за коментар, хоча автор публікації завжди повідомляється про це в будь-якому випадку.)
Ilmari Karonen

7

Перл - 47 (було 52) символів

sub rand7{($x=5*&rand5+&rand5-3)<24?int($x/3):&rand7} 

Плюс я можу скористатися потрійним оператором І рекурсією. Найкращий день!

ОК, 47 знаків, якщо ви використовуєте мод замість div:

sub rand7{($x=5*&rand5+&rand5)<27?$x%7+1:&rand7} 

Так близько ... замініть 30 на 27 (= 6 + 21), і ви отримаєте ідеально рівномірний розподіл. О, і ви можете опустити два останні &знаки, щоб зменшити його до 46 символів (включаючи пробіл, який ставить вашій поточній версії 48).
Ільмарі Каронен

7

JavaScript, 42

Rand7=f=_=>(x=Rand5()+Rand5()*5-5)>7?f():x

Бонусна річ ES5:

Rand7=eval.bind(0,'for(;x=Rand5()+Rand5()*5-5,x>7;);x')

6

Ruby - 54 символи (на основі рішення Дена Макграта, використовуючи цикл)

def rand7;x=8;while x>7 do x=rand5+5*rand5-5 end;x;end

Ruby - 45 символів (той же розчин, використовуючи рекурсію)

def rand7;x=rand5+5*rand5-5;x>7 ?rand7: x;end

Можна скоротити на 1 знак за допомогою (x=rand5+5*rand5-5)>7?.
Ларс Хагсет


4

У загальному Lisp 70 символів:

(defun rand7()(let((n(-(+(rand5)(* 5(rand5)))5)))(if(> n 7)(rand7)n)))

Дужки займають більше місця, ніж я хотів би.


Приємно. Ви можете видавити ще двох символів, зробивши глобальну змінну:(defun rand7()(setq n(-(+(rand5)(* 5(rand5)))5))(if(> n 7)(rand7)n))
Доктор Біль

Ще краще:(defun rand7()(if(>(setq n(-(+(rand5)(* 5(rand5)))5))7)(rand7)n))
Доктор Біль

4

В c / c ++ з використанням відбору відторгнення

int rand7(){int x=8;while(x>7)x=rand5()+5*rand5()-5;return x;}

62 символи.


@barrycarter: Умова така while(x>7), щоб її задовольняли лише числа у дійсному діапазоні.
mellamokb

Моє ліжко. Видалено мій німий коментар.
barrycarter

@barry І тоді ти залишив ще один. ;)
Матін Ульхак

Мені знадобилось кілька хвилин, щоб зрозуміти, як математика тут виробляє рівномірно випадковий розподіл, який може бути використаний для вибірки відхилення.
Даніель

3

Переклад на PHP, з відповіді, опублікованої Деном МакГратом.

function Rand7(){$x=8;while($x>7)$x=rand5()+5*rand5()-5;return $x;}

67 символів.


Чи не повинно це бути префіксом слово "функція" (і пробіл)?
jtjacques

Так ... а зараз це 67 символів ...
Марк-Франсуа

3

R, 34 символи

У R (мова, побудована для статистичних обчислень), навмисно шахрайське рішення:

# Construct a Rand5 function
Rand5 <- function() sample(seq(5),1)
# And the golf
Rand7=function(r=Rand5())sample(1:(r/r+6),1)
# Or (same character count)
Rand7=function(r=Rand5())sample.int(r/r+6,1)
# Or even shorter(thanks to @Spacedman)
Rand7=function()sample(7)[Rand5()]

Завдяки лінивій оцінці аргументів я усунув крапку з комою та дужки.

Виведіть більше 10 ^ 6 повторів:

> test <- replicate(10^6,Rand7())
> table(test)
test
     1      2      3      4      5      6      7 
142987 142547 143133 142719 142897 142869 142848 

library(ggplot2)
qplot(test)

гістограма результатів


2
Якщо ви збираєтеся бути ошуканішими, ви можете бути найкращим шахраєм, яким ви можете бути:Rand7=function(){r=Rand5();sample(7)[r]}
Spacedman

Якщо ви збираєтесь це робити, навіщо турбуватися проміжним сховищем? Rand7=function(){sample(7)[Rand5()]}
Брайан Діггс

@BrianDiggs Залежність шляху в дії .... :-)
Арі Б. Фрідман

3

scala, 47, 40 59 символів:

def rand7:Int={val r=5*(rand5-1)+rand5
if(r<8)r else rand7}

з 2 входами від rand5:

\ 1 2 3 4 5 
1 1 2 3 4 5  
2 6 7 8 ..
3 11 ..
4 ..
5

Помножую перше-1 на 5, а друге додаю. Більшість результатів ігноруються та ведуть до нового розрахунку. Результатом має стати рівномірний розподіл значень від 1-25, з яких я вибираю лише перші 7. Я міг би прийняти перший 21 зі створенням модуля, але це призведе до більш тривалого коду.

історичний код, який провалився, але не дуже очевидно. Дякуємо Ілмарі Каронен за те, що вона вказала:

def rand7=(1 to 7).map(_=>rand5).sum%7+1

Дякую Йошитеру Такешиті, за цей підхід, який набув масштабу-2.8.0, який зробив «суму» такою простою. Моє рішення до:

def rand7=((0/:(1 to 7))((a,_)=>a+rand5-1))%7+1

rand5:

val rnd = util.Random 
def rand5 = rnd.nextInt (5) + 1


Користувач Yoshiteru Takeshita запропонував зменшити до 40 символів для Scala 2.8.0 або пізнішої версіїdef rand7=(1 to 7).map(_=>rand5).sum%7+1
Пітер Тейлор

Це рішення також не є правильним, див. Коментарі до відповіді glowcoder .
Ільмарі Каронен

@IlmariKaronen: Ти маєш рацію - я переробив своє рішення.
користувач невідомий

3

C ++

int Rand4()
{
    int r = Rand5();
    return r > 4 ? Rand4() : r;
}

inline int Rand8()
{    
    return (Rand4() - 1) << 2 + Rand4();
}

int Rand7()
{
    int r = Rand8();
    return r > 7 ? Rand7() : r;
}

C ++ (109)

Гольф

int Rand4(){int r=Rand5();return r>4?Rand4():r;}int Rand7(){int r=Rand4()-1<<2+Rand4();return r>7?Rand7():r;}

Я не думаю, що ви можете назвати це "одним вкладишем", оскільки крапкою з комою визначено рядок коду на C ++.
Пітер Олсон

@Peter Ну добре, це навіть не вимагає одноразових лайнерів більше.
Mateen Ulhaq

Він повернув число від 1 до 8.
jimmy23013

2

Переклад на Javascript, з відповіді, опублікованої Деном МакГратом.

function Rand7(){x=8;while(x>7)x=rand5()+5*rand5()-5;return x}

62 символи


1
function Rand7(){for(x=8;x>7;x=rand5()+5*rand5()-5);return x}трохи коротше: P
JiminP

2

JavaScript, 85

function Rand7(){for(x=0,i=1;i<8;x^=i*((k=Rand5())%2),i*=1+(k<5));return x?x:Rand7()}

Я знаю, що відповідь коротша, але я хотів показати тест цієї головоломки. Виявляється, лише відповідь Клайда Лобо з використанням вибірки відхилення Дена МакГрата є правильною (між відповідями JS).


2

С ++

int Rand7()
{
    int r = Rand5();
    int n = 5;
    do {
        r = (r - 1) * 5 + Rand5();
        int m = n * 5 / 7 * 7;
        if (r <= m) {
            return r % 7 + 1;
        }
        r -= m;
        n = n * 5 - m;
    } while (1);
}

Розподіл чисел (1000000 цілих чисел):

142935 142751 142652 143299 142969 142691 142703

Середня кількість дзвінків до Rand5 () на кожне генероване ціле число становить приблизно 2,2 (2 до 10+).

1 2      3      4     5    6   7 8  9 10
0 840180 112222 44433 2212 886 0 60 6 1

2

У Java (або, напевно, C / C ++)

  • використовуючи формулу генерації Олександру, в 65 символів:

    int rand7(){int x=rand5()*5+rand5()-6;return x>20?rand7():x/3+1;}
    
  • використовуючи формулу покоління Дена МакГрата, в 60 символів

    int rand7(){int x=rand5()+5*rand5()-5;return x>7?rand7():x;}
    


1

Пітона, 56 37 символів

Ще одне рішення, яке може бути неправильним, в Python:

rand7 = lambda: sum(rand5() for i in range(7)) % 7 + 1

Це здається занадто простим, але коли я намагаюся:

counter = [0] * 7
for i in range(100000):
     counter[rand7()] += 1

Я отримую досить рівномірний розподіл (все між 14000 і 14500).

Гаразд, зараз, як хтось проголосував за цю посаду: чи справді це рішення правильне? Я більше публікував це тут, щоб змусити людей його критикувати. Ну, якщо це правильно, моя версія для гольфу була б:

rand7=lambda:eval("+rand5()"*7)%7+1

що виходить до 37 знаків.


Ваше рішення невірне: ви базуєте своє рішення на 7 рулонах справедливого 5-стороннього штампу, що означає, що є 5 ^ 7 (від 5 до 7-ї потужності) однозначних результатів. Оскільки це не кратне значення 7, ви не можете повернути 7 однозначних результатів. Я не думаю, що існує легка формула того, що ти повертаєш; ви можете змусити обчислити або обробити його вручну на менших числах (переверніть 3 монети (H = 1, T = 2) і підсумовуйте результати).
Жил 'SO- перестань бути злим'

1
Нічого так, розподілення, яке ви генеруєте, хоч і не є рівномірним, надзвичайно близьке: точна частка ймовірностей кожного числа дорівнює {1: 11177, 2: 11172, 3: 11158, 4: 11144, 5: 11144, 6: 11158, 7: 11172}
Омар



1

Perl, 43 символи, ітеративне відбирання відбору

sub rand7{1while($_=5*&rand5-rand5)>6;$_+1}

Це дає попередження про Ambiguous use of -rand5 resolved as -&rand5(), але працює правильно. Попередження &до другого rand5дзвінка фіксує його ціною одного удару. (І навпаки, інше &також можна видалити, якщо rand5 воно було визначено ()прототипом.)

Пс. Наступна 46-char версія приблизно втричі швидша:

sub rand7{1while($_=5*&rand5-rand5)>20;$_%7+1}

1

Ява - 66 символів

int rand7(){int s;while((s=rand5()*5+rand5())<10);return(s%7+1);}

Більше, ніж попередня програма, але я думаю, що ця повертає рівномірно розподілені числа за менший час.


1

PostScript (46)

Тут використовується кодування двійкового маркера, отже, ось шістнадцятковий:

00000000  2f 72 61 6e 64 37 7b 38  7b 92 38 37 92 61 7b 92  |/rand7{8{.87.a{.|
00000010  40 7d 69 66 92 75 32 7b  72 61 6e 64 35 7d 92 83  |@}if.u2{rand5}..|
00000020  35 92 6c 92 01 35 92 a9  7d 92 65 7d 92 33        |5.l..5..}.e}.3|
0000002e

Щоб спробувати це, ви також можете завантажити його .

Ось нерозроблений та коментований код разом із кодом тестування.

% This is the actual rand7 procedure.
/rand7{
  8{                      % potentialResult
    % only if the random number is less than or equal to 7, we're done
    dup 7 le{             % result
      exit                % result
    }if                   % potentialResult
    pop                   % -/-
    2{rand5}repeat        % randomNumber1 randomNumber2
    5 mul add 5 sub       % randomNumber1 + 5*randomNumber2 - 5 = potentialResult
  }loop
}def

%Now, some testing code.

% For testing, we use the built-in rand operator; 
% Doesn't really give a 100% even distribution as it returns numbers
% from 0 to 2^31-1, which is of course not divisible by 5.
/rand5 {
  rand 5 mod 1 add
}def

% For testing, we initialize a dict that counts the number of times any number
% has been returned. Of course, we start the count at 0 for every number.
<<1 1 7{0}for>>begin

% Now we're calling the function quite a number of times 
% and increment the counters accordingly.
1000000 {
  rand7 dup load 1 add def
}repeat

% Print the results
currentdict{
  2 array astore ==
}forall

-1
int result = 0;

for (int i = 0; i++; i<7)
    if (((rand(5) + rand(5)) % 2) //check if odd
        result += 1;

return result + 1;

2
Це не дасть рівномірного розподілу. Подивіться на розподіл rand (5) + rand (5) на 10000 ітерацій, щоб дізнатися, чому
gnibbler

Результатом може бути будь-яке число від 1 до 8 у вашому коді ...
Омар

Плюс, як сказав гніблер, розподіл не є рівномірним: (rand (5) + rand (5))% 2 зміщений до 0, він виробляє 0 13 разів на кожні 12 разів, він виробляє 1; тобто ймовірності пропорційні {0: 13, 1: 12}. При цьому позначення пропорції для вашої функції пропорційні {1: 62748517, 2: 405451956, 3: 1122790032, 4: 1727369280, 5: 1594494720, 6: 883104768, 7: 271724544, 8: 35831808} (досить сильно перекошені до більша кількість). Або, виправляючи цикл для запуску 6 разів, {1: 4826809, 2: 26733096, 3: 61691760, 4: 75928320, 5: 52565760, 6: 19408896, 7: 2985984}
Омар

-1

R (30 символів)

Визначте rand7:

rand7=function(n)sample(7,n,T)

Оскільки R було написано з урахуванням статистичного аналізу, це завдання є тривіальним, і я використовую вбудовану функцію sampleіз заміною, встановленою на TRUE.

Вибірка зразка:

> rand7(20)
 [1] 4 3 6 1 2 4 3 2 3 2 5 1 4 6 4 2 4 6 6 1
> rand7(20)
 [1] 1 2 5 2 6 4 6 1 7 1 1 3 7 6 4 7 4 2 1 2
> rand7(20)
 [1] 6 7 1 3 3 1 5 4 3 4 2 1 5 4 4 4 7 7 1 5

1
Це говорить про те, що ви повинні використовувати Rand5. Не говорить як, але ви повинні ним скористатися ...
Spacedman

@Spacedman Так, я явно проігнорував це. Це використання за допомогою не-посилання.
Андрі

-1

Groovy

rand7={if(b==null)b=rand5();(b=(rand5()+b)%7+1)}

приклад розподілу понад 35000 ітерацій:

[1:5030, 2:4909, 3:5017, 4:4942, 5:5118, 6:4956, 7:5028]

Чи погано, що це державно?



-1

Як щодо цього?

int Rand7()
{
    return Rand5()+ Rand5()/2;
}

Якою б мовою це не був, чи робить її /оператор цілочисельну математику? Що станеться з вашими результатами, якщо це робить математика з десятковою, крапкою чи цілим числом?
kojiro

Припускаючи , цілочисельне ділення, ця функція має наступний розподіл: [2/25, 4/25, 5/25, 5/25, 5/25, 3/25, 1/25]. Не зовсім рівномірно.
прим

прим - це правильно. додавання випадкових чисел, як правило, перекручує ймовірності в бік середніх значень.
гніблер

-1

Ява - 54

int m=0;int rand7(){return(m=m*5&-1>>>1|rand5())%7+1;}

Тест на розповсюдження: [1000915, 999689, 999169, 998227, 1001653, 1000419, 999928]

Алгоритм:

  • Зберігайте глобальну змінну
  • помножте на 5, так що ви отримаєте 5 місць, вільних, принаймні значущим кінцем
  • Укорочуйте біт знака, щоб він став позитивним (не потрібно, якщо підтримуються неподписані номери)
  • Модуло 7 - це відповідь

> Цифри вже взаємно не співвідносяться, а індивідуально абсолютно випадкові.


-1

Ruby (43 байти)

def rand7;(0..7).reduce{|i|i+rand5}%7+1;end

Рішення cemper93, перенесене на Ruby, на три байти коротше;) (34 байт)

def rand7;eval("+rand5"*7)%7+1;end

-3

Код C / C ++, основний код має лише один рядок!

static unsigned int gi = 0;

int rand7()
{
    return (((rand() % 5 + 1) + (gi++ % 7)) % 7) + 1;
}

//call this seed before rand7
//maybe it's not best seed, if yo have any good idea tell me please
//and thanks JiminP again, he remind me to do this
void srand7()
{
    int i, n = time(0);
    for (i = 0; i < n % 7; i++)
        rand7();
}

Srand7 () є насінням rand7, повинен викликати цю функцію перед rand7, як і виклик srand перед rand в C.

Це дуже добре, тому що він викликає rand () лише один раз, і жодна цикла, не витрачає зайвих спогадів.

Поясню це: розглянемо цілий масив розміром 5:

1st get one number from 1 2 3 4 5 by rand5
2nd get one number from 2 3 4 5 6
3rd get one number from 3 4 5 6 7
4th get one number from 4 5 6 7 1
5th get one number from 5 6 7 1 2
5th get one number from 6 7 1 2 3
7th get one number from 7 1 2 3 4

Таким чином, ми отримали ТАБЛИЦЮ, кожен з 1-7 з'являється в ній 5 разів і має всі 35 чисел, тому ймовірність кожного числа становить 5/35 = 1/7. І наступного разу,

8th get one number from 1 2 3 4 5
9th get one number from 2 3 4 5 6
......

Через достатньо разів ми можемо отримати рівномірний розподіл 1-7.

Отже, ми можемо виділити масив для відновлення п’яти елементів 1-7 за допомогою циклу-зсуву вліво і отримувати одне число з масиву кожен раз за допомогою rand5. Натомість ми можемо генерувати всі сім масивів раніше, використовуючи їх круговим способом. Код також простий, має багато коротких кодів.

Але ми можемо використовувати властивості% операції, тому таблиця 1-7 рядків еквівалентна (rand5 + i)% 7, тобто: a = rand ()% 5 + 1 є rand5 мовою C, b = gi ++ % 7 створює всі перестановки в таблиці вище, а 0 - 6 замінює 1 - 7 c = (a + b)% 7 + 1, генерує 1 - 7 рівномірно. Нарешті, ми отримали цей код:

(((rand() % 5 + 1) + (gi++ % 7)) % 7) + 1 

Але ми не можемо отримати 6 та 7 під час першого дзвінка, тому нам потрібне насіння, якесь, як srand для rand в C / C ++, щоб змінити перестановку для першого офіційного виклику.

Ось повний код для тестування:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

static unsigned int gi = 0;

//a = rand() % 5 + 1 is rand5 in C language,
//b = gi++ % 7 generates all permutations,
//c = (a + b) % 7 + 1, generates 1 - 7 uniformly.
//Dont forget call srand7 before rand7
int rand7()
{
   return (((rand() % 5 + 1) + (gi++ % 7)) % 7) + 1;
}

//call this seed before rand7
//maybe it's not best seed, if yo have any good idea tell me please
//and thanks JiminP again, he remind me to do this
void srand7()
{
    int i, n = time(0);
    for (i = 0; i < n % 7; i++)
        rand7();
}

void main(void)
{
    unsigned int result[10] = {0};
    int k;

    srand((unsigned int)time(0)); //initialize the seed for rand
    srand7() //initialize the rand7

    for (k = 0; k < 100000; k++)
        result[rand7() - 1]++;

    for (k = 0; k < 7; k++)
        printf("%d : %.05f\n", k + 1, (float)result[k]/100000);
}

Він 'проходить' тест, але це не означає, що це хороша випадкова функція. Чи можу я отримати 6або 7зателефонувавши один раз ?
JiminP

Але є хороші види і погані види наближення. І цей код поганий - тому що він не дає рівномірного розподілу, коли його викликають лише один раз. Якщо хтось написав щось на кшталт int main(){if(rand7()==6) printf("Hello, world!");}, наближення за допомогою циклу надрукує "Привіт, світ!" 1 раз у 7 разів, але ваш код - ні.
JiminP

дякую @JiminP! ви праві на 6,7 в перший раз. Мені потрібно насіння, щоб переставити його перед викликом rand7, насіння так само, як сранд в C / C ++. я виправив свій код, і ще раз дякую !!!
Шон

хм .... srand10 не працює, останні 3 номери не можуть потрапити на 10, 20, 30 ... позицій. Вибачте @JiminP, але як це змінити? Я думаю, що це сподіваний спосіб.
Шон

2
Різні дзвінки до цієї функції не залежать один від одного. Спеціалізації тут цього не вимагає, але це, як правило, очікується від генераторів випадкових чисел. Інакше, можна сказати, повернути випадкове рівномірне число вперше, а в майбутньому дзвінки просто повертаються (попередній + 1)% 7 ...
Омар
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.