Выбор случайных чисел эффективно

У меня есть метод, который использует случайные выборки для приближения вычисления. Этот метод называют миллионами времен, таким образом, его очень важное, что процесс выбора случайных чисел эффективен.

Я не уверен как быстро javas Random().nextInt действительно, но моя программа, кажется, не извлекает выгоду так же, как я хотел бы ее также.

При выборе случайных чисел я делаю следующее (в полу псевдокоде):

// Repeat this 300000 times
Set set = new Set();
while(set.length != 5)
    set.add(randomNumber(MIN,MAX));

Теперь, это, очевидно, имеет плохое время выполнения худшего случая, поскольку случайная функция в теории может добавить дублированные числа для вечности, таким образом остающейся в цикле с условием продолжения навсегда. Однако числа выбраны из {0.. 45}, таким образом, дублированное значение по большей части маловероятно.

Когда я использую вышеупомянутый метод, только на 40% быстрее, чем мой другой метод, который не приближается, но приводит к корректному результату. Это, выполнил ~ 1 миллион раз, таким образом, я ожидал, что этот новый метод будет по крайней мере на 50% быстрее.

У Вас есть какие-либо предложения для более быстрого метода? Или возможно Вы знаете более эффективного способа поколения ряд случайных чисел.

Для разъяснения вот, эти два метода:

// Run through all combinations (1 million). This takes 5 seconds
 for(int c1 = 0; c1 < deck.length; c1++){
    for(int c2 = c1+1; c2 < deck.length; c2++){
     for(int c3 = c2+1; c3 < deck.length; c3++){
        for(int c4 = c3+1; c4 < deck.length; c4++){
         for(int c5 = c4+1; c5 < deck.length; c5++){
             enumeration(hands, cards, deck, c1, c2, c3, c4, c5);
         }
            } 
      }     
   }
   }

// Approximate (300000 combinations). This takes 3 seconds
Random rand = new Random();
HashSet<Integer> set = new HashSet<Integer>();
int[] numbers = new int[5];
while(enumerations < 300000){
set.clear();
while(set.size() != 5){
    set.add(rand.nextInt(deck.length));
}
Iterator<Integer> i = set.iterator();
int n = 0;
while(i.hasNext()){
    numbers[n] = i.next();
    n++;
}

После некоторого тестирования и профилирования, я нашел, что этот метод был самым эффективным:

Random rand = new Random();
int[] numbers = new int[5];
ArrayList<Integer> list = new ArrayList<Integer>();
while(enumerations < 300000){
 while(list.size() != 5) {
     int i = rand.nextInt(deck.length);
        if(!list.contains(i)) list.add(i);
 }
 int index = 0;
 for(int i : list){ numbers[index] = i; index++; }
 enumeration(hands, cards, deck,numbers);
}
12
задан 5 revs, 2 users 100% 26 March 2010 в 21:45
поделиться

8 ответов

Вы можете попробовать использовать существующую реализацию Java ( или эту ) для Mersenne Twister .

Имейте в виду, что большинство MT не криптографически безопасны.

11
ответ дан 2 December 2019 в 07:02
поделиться

Обычный метод - начать со списка всех возможных входов и произвольно выбирать из него, удаляя все по ходу. Таким образом, нет риска выбрать дубликат и выполнить цикл в течение неизвестного времени. Конечно, этот метод работает только с дискретными данными, но, к счастью, с целыми числами. Также помните, что выбор и удаление вашего списка (или другой структуры данных) должны быть O (1), если это возможно, поскольку вы фокусируетесь на скорости.

2
ответ дан 2 December 2019 в 07:02
поделиться

Вы можете использовать линейное сравнение в качестве генератора случайных чисел: http://en.wikipedia.org/wiki/Linear_congruential_generator [но учтите их статистические недостатки]

Вам нужно только вычислить (x + c)% m для каждого числа. Тем не менее, по моему опыту, создание объектов (как вы могли бы делать с каждым вызовом new Set и add, в зависимости от того, какую реализацию вы используете) может стоить вам больше скорости, чем вызов nextInt (). Возможно, вам стоит попробовать профилировщик, например, этот: http://www.eclipse.org/tptp/

2
ответ дан 2 December 2019 в 07:02
поделиться

У меня нет информации о вашей реальной проблеме, и я не слишком хорошо знаю Java (просто ковыряюсь). Однако мне кажется, что вы пытаетесь создать оценщик рук для покера, и эта ветка http://pokerai.org/pf3/viewtopic.php?f=3&t=16 содержит очень быструю java-руку. оценщики. Надеюсь, что часть этого кода может помочь.

1
ответ дан 2 December 2019 в 07:02
поделиться

Если вам мешает пропускать дубликаты, вы можете решить эту проблему, создав список всех значений карт. , а затем удаление из списка по мере выбора карточек и выбор случайного числа в меньшем диапазоне в следующий раз. Примерно так:

// Assuming we're just numbering all the cards 0 to 51. This could be more sophisticated, of course.
ArrayList cards=new ArrayList(52);
for (int x=0;x<52;++x)
  cards=new Integer(x);

Integer[] hand=new Integer[5];
for (int h=0;h<5;++h)
{
  // Pick a card from those remaining
  int n=random.nextInt(cards.size());
  hand[h]=cards.get(n);
  // Remove the picked card from the list
  cards.remove(n);
}

При первом розыгрыше card.get (n) вернет n, независимо от того, какое n. Но с этого момента значения будут удаляться, поэтому card.get (3) может возвращать 7 и т. Д.

Создание списка и удаление из него добавляет кучу накладных расходов. Я предполагаю, что если вы выбираете только 5 карт за раз, вероятность коллизий достаточно мала, и устранение дубликатов после того, как вы их найдете, будет быстрее, чем их предотвращение. Даже при последнем розыгрыше вероятность дублирования составляет всего 4/52 = 1/13, так что вы вообще редко попадете в дубликат, и вероятность того, что два розыгрыша подряд будут дубликатами, будет крошечной. Все зависит от того, сколько времени требуется для генерации случайного числа по сравнению с тем, сколько времени требуется для настройки массива и выполнения удаления. Самый простой способ узнать это - провести несколько экспериментов и измерить. (Или профиль!)

1
ответ дан 2 December 2019 в 07:02
поделиться

Похоже, вы хотите выбрать комбинацию k - из набора S без замены, с S , имеющим n различных значений, k = 5 и n = 52. Вы можете перемешать () весь набор и выберите элементы k (как предлагает @Tesserex) или pick () k элементов, избегая дублирования (как вы показали). Вы захотите профилировать как в вашей конкретной среде, так и для выбранного вами генератора. Я часто, но не всегда, вижу небольшое преимущество pick () .

private static final Random rnd = new Random();
private static final int N = 52;
private static final int K = 5;
private static final List<Integer> S = new ArrayList<Integer>(N);
static {
    for (int i = 0; i < N; i++) {
        S.add(i + 1);
    }
}
private final List<Integer> combination = new ArrayList<Integer>(K);

...

private void shuffle() {
    Collections.shuffle(S, rnd);
    combination.addAll(S.subList(0, K));
}

private void pick() {
    for (int i = 0; i < K; i++) {
        int v = 0;
        do {
            v = rnd.nextInt(N) + 1;
        } while (combination.contains(v));
        combination.add(v);
    }
}
5
ответ дан 2 December 2019 в 07:02
поделиться

Не пытайтесь разработать свой известный генератор случайных чисел. Вместо этого используйте известный, например SecureRandom:

http://www.owasp.org/index.php/Using_the_Java_Cryptographic_Extensions

0
ответ дан 2 December 2019 в 07:02
поделиться

Никогда не угадывай, всегда измеряй.

 long time = System.getCurrentMilliseconds();
 Random().nextInt()
 System.out.println(System.getCurrentMilliseconds() - time);

Кроме того, вы никогда не должны полагаться на то, насколько редко будет происходить известная ошибка, просто используйте защитный код, чтобы этого не произошло. Обнаружьте дубликат, и если это дубликат, не добавляйте его и пропустите итерацию с помощью оператора continue .

Что касается самых быстрых методов и случайных чисел ... Вы не можете получить случайные числа в Java Math.random () . Вы можете получить только псевдослучайные числа. Насколько быстро вы хотите, чтобы это происходило, зависит от того, насколько, казалось бы, случайным вам нужно, чтобы они появлялись. Самый быстрый способ сгенерировать псевдослучайное число - это сдвиг и сложение битов на основе начального значения, такого как System.getCurrentMilliSeconds () Кроме того, генерация псевдослучайных чисел уже выполняется довольно быстро, поскольку это просто в любом случае необработанная арифметика ЦП, так что вы, вероятно, будете достаточно счастливы, когда увидите, сколько миллисекунд требуется для генерации таковой с помощью Math.random () .

0
ответ дан 2 December 2019 в 07:02
поделиться
Другие вопросы по тегам:

Похожие вопросы: