Генератор случайных, но предсказуемых чисел? [C ++] - PullRequest
3 голосов
/ 14 февраля 2012

Ну, я действительно не знаю, как искать то, что я ищу.Google дает массу результатов, но ни один из них не соответствует моим критериям.

Поэтому я спрашиваю здесь: есть ли какой-нибудь известный фрагмент кода, который может создать число, которое является предсказуемым, выглядит случайным и основано нана «семени» (в моем случае это метка времени Unix) и между указанным диапазоном?

Я хочу иметь возможность создавать прогноз погоды в сценарии для игры, которую я кодирую (но мне нужноКод C ++, который я могу портировать, я не думаю, что многие здесь знакомы с языком сценариев 'PAWN' (он же МАЛЕНЬКИЙ)? :)).Идентификаторы погоды варьируются от 0 до ~ 100, включая некоторые устаревшие идентификаторы (поэтому мое решение будет заключаться в создании массива, содержащего действительные идентификаторы погоды, чтобы нам не нужно было беспокоиться об этих BAD_ID, давайте не будем усложнять функцию).

Я мог бы сделать такую ​​формулу, но проблема в прошлом, которую я имел, состояла в том, что погода менялась слишком быстро (как каждую секунду, хотя я где-то потерял код: /), и на данный момент я действительно изидеи о том, как я собираюсь сделать такую ​​формулу.

Любые предложения тоже очень ценятся!

Ответы [ 7 ]

2 голосов
/ 14 февраля 2012

Посмотрите на реализацию C генератора случайных чисел , используемого VB6 .Он идеально подходит для игр, потому что он генерирует довольно правдоподобные случайные последовательности, но использует начальное число, и одно и то же начальное число всегда генерирует одну и ту же последовательность.Поэтому в файлах игровых данных вы можете сохранить набор начальных значений, которые дадут вам известные (но случайные) последовательности, которые вы можете легко воспроизвести.

Вот реализация, которая возвращает значения в диапазоне:

typedef int Int32;
typedef unsigned int UInt32;

class CRnd
{
    private:
        static const UInt32 INITIAL_VALUE = 0x50000;
        static const UInt32 INCREMENT = 0xC39EC3;
        static const UInt32 MULTIPLIER = 0x43FD43FD;

    private:
        UInt32 m_nRnd;

    public:
        CRnd () { m_nRnd = INITIAL_VALUE; };
        CRnd ( IN UInt32 nSeed ) { m_nRnd = nSeed; };
        virtual ~CRnd () {};

        Int32 Get ( IN Int32 nFrom, IN Int32 nTo )
        {
            if ( nTo < nFrom ) // nFrom should be less than nTo
            {
                Int32 nTmp = nTo;

                nTo = nFrom;
                nFrom = nTmp;
            }
            else if ( nTo == nFrom )
            {
                return ( nTo );
            }

            m_nRnd = ( m_nRnd * MULTIPLIER + INCREMENT ) & 0xFFFFFF;

            float fTmp = (float) m_nRnd / (float) 16777216.0;

            return ( (Int32) ( ( fTmp * ( nTo - nFrom + 1 ) ) + nFrom ) );
        };

        void SetSeed ( IN UInt32 nSeed ) { m_nRnd = nSeed; };
        UInt32 GetSeed () { return ( m_nRnd ); };
};
2 голосов
/ 14 февраля 2012

Очевидно, что число не может быть одновременно «предсказуемым» и «случайным» - это прямо противоречивые термины.

Я предполагаю, что вы имеете в виду число, которое является одновременно и детерминированным semirandom.

К счастью для вас, это то, что генерируют генераторы псевдослучайных чисел (PRNG): когда они запускаются с согласованным начальным числом, они выдают одинаковый вывод.ваше семя с srandom, затем с помощью random() % MAX_VALUE, чтобы получить число от 0 до MAX_VALUE.Если вы получили «плохую ценность», просто идите снова.Повторите без повторного заполнения столько раз, сколько хотите.

2 голосов
/ 14 февраля 2012

Посмотрите srand и rand для начала.

C ++ 11 также включает в себя множество более сложных алгоритмов, но для базовых потребностей достаточно двух вышеуказанных.удерживайте числа в диапазоне от 0 до n, используйте оператор %.

1 голос
/ 14 февраля 2012

Если вам нужно медленно изменяющееся значение, вы можете использовать функцию шума, такую ​​как Perlin Noise .

0 голосов
/ 14 февраля 2012

Я думаю, вы можете использовать rand для генерации случайных чисел.Однако вы можете присвоить srand то же значение, что и, скажем, 99, так что ваши числа будут случайными, но предсказуемыми каждый раз.

0 голосов
/ 14 февраля 2012

Проблема с srand и rand заключается в том, что только их сигнатуры вызовов (а не значения, которые они генерируют) определяются стандартом C. Если вам нужны переносимые и детерминированные псевдослучайные числа, вы должны реализовать их самостоятельно. Вот класс, написанный на C ++, который основан на классах, найденных в Numeric Recipes, и является полностью переносимым. Вы можете создать поток случайных чисел с начальным числом, если хотите. Я жестко закодирую это начальное число вместо использования времени на тот случай, если снова и снова хочу одну и ту же псевдослучайную последовательность. Вы также можете использовать метод RandomInteger(a,b), чтобы получить целые числа на полуоткрытом интервале [a, b).

class RandomNumberStream
{
private:
  unsigned long long u,v,w;

public:
  RandomNumberStream(int n=1);
  double RandomDouble();
  double RandomDouble(double a, double b);
  unsigned long long RandomInteger();
  unsigned long long RandomInteger(int a, int b);
private:
  unsigned long long int64();
} ;



RandomNumberStream::RandomNumberStream(int n)
{
  v = 4101842887655102017LL;
  w = 1;

  u = n^v; int64();
  v =   u; int64();
  w =   v; int64();
}
double RandomNumberStream::RandomDouble()
{
  return int64() * 5.42101086242752217E-20f;
}
double RandomNumberStream::RandomDouble(double a, double b)
{
  return int64() * 5.42101086242752217E-20f * (b-a) + a;
}
unsigned long long RandomNumberStream::RandomInteger()
{
  return int64();
}
unsigned long long RandomNumberStream::RandomInteger(int a, int b)
{
  return a + int64() % (b-a);
}
unsigned long long RandomNumberStream::int64()
{
  u  = u * 2862933555777941757LL + 7046029254386353087LL;
  v ^= v>>17; v ^= v<<31; v ^= v>>8;
  w  = 4294957665U*(w & 0xffffffff) + (w>>32);
  unsigned long long x = u^(u<<21); x ^= x>>35; x ^= x<<4;
  return (x+v)^w;
}
0 голосов
/ 14 февраля 2012

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

В частности, вы хотите хешировать целые числа в целые числа.Вы можете подобрать такую ​​функцию здесь .Я рекомендую ту, которая называется «32-битная целочисленная хэш-функция Роберта Дженкинса» - у меня всегда хорошо получалось.

В итоге вы получите что-то вроде:

int time_index = 3;
int weather_state = integer_hash_function(time_index) % (MAX_VALUE - MIN_VALUE + 1) + MIN_VALUE

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

...