Генерация чисел в промежутках

Тема в разделе "WASM.A&O", создана пользователем Entropy, 24 сен 2025.

  1. Entropy

    Entropy Member

    Публикаций:
    0
    Регистрация:
    23 авг 2020
    Сообщения:
    200
    Мне дано 6 чисел: 65, 96, 124, 152, 183, 218 я хотел используя готовый генератор псевдослучайных чисел из какой нибудь библиотеки, сгенерировать случайные числа,но сделать это так что эти числа не совпадали с исходными 6 числами,и находились в промежутки между ними,но проблема в том если чисел дано было не 6 а 150 000 насколько это было сложно реализовать ?
     
  2. Research

    Research Active Member

    Публикаций:
    1
    Регистрация:
    6 янв 2024
    Сообщения:
    313
    Вместо того чтобы генерировать числа и проверять их, нужно сразу генерировать число из правильного интервала.
     
  3. f13nd

    f13nd Well-Known Member

    Публикаций:
    0
    Регистрация:
    22 июн 2009
    Сообщения:
    2.073
    Интервал кодируется двумя числами, число кодируется одним числом. Сравнение числа с интервалом это два сравнения, сравнение числа с числом - одно. Для 150000 чисел будет 150001 интервалов. Получается, что самый оптимальный с точки зрения использования памяти и машинного времени способ - самый тупой из всех: хранить запрещенные числа и каждое значение гпсч сравнивать с каждым из них.
     
    Mikl___ нравится это.
  4. aa_dav

    aa_dav Active Member

    Публикаций:
    0
    Регистрация:
    24 дек 2008
    Сообщения:
    570
    Если интервалы почти равномерные можно бросать кубик два раза - сперва в какой интервал выпадает очередное число, а потом какое оно внутри этого интервала.

    Если границы интервалов не сильно велики по размеру можно снизить сложность до линейной построив вспомогательный массив смещений.
    Пусть граничных чисел 3 - назовём это числом N.
    Для простоты вычтем из них первое число чтобы базис отталкивался от 0 (потом всегда можно добавить чтобы вернуться в исходную область).
    Пусть получатся 0, 3 и 8.
    Последний элемент 8 - значит нам нужен вспомогательный массив размером [0..8-N] и это будет:
    0->1
    1->2
    2->4
    3->5
    4->6
    5->7
    Т.е. мы линейно заполняем его увеличивающимися числами, но там где должно получится интервальное число прибавляем еще 1 чтобы его пропустить.
    Если массив интервалов отсортирован по возрастанию, то строится тоже линейным проходом.
    Теперь останется только выкидывать числа от 0 до 5 и берем результат по этому индексу в этом массиве и добавлять поправку которую вычли в начале.

    Если же границы интервалов сильно велики и не получится промежуточный массив выделить разумный в памяти, то можно тем не менее удерживать сложность в рамках O(N*logN) если по сути делать то же самое, если превратить этот массив в похожую по смыслу структуру данных где будем искать бинарным поиском в каком интервале оказались и извлекать из него нужные смещения и добавки.
     
  5. R81...

    R81... Active Member

    Публикаций:
    0
    Регистрация:
    1 фев 2020
    Сообщения:
    176
    БитМап
    --- Сообщение объединено, 24 сен 2025 ---
    дольше отсортированным списком - самый простой поиск по деревяшке.
     
  6. Entropy

    Entropy Member

    Публикаций:
    0
    Регистрация:
    23 авг 2020
    Сообщения:
    200
    aa_dav, получается что нужно найти разность между интеревальными числами,то есть
    96 - 65 = 31 (1 интеревал 65,96
    152 - 124 = 28 (2 интеревал 124,152
    218 - 183 = 35 (3 интеревал 183,218
    если ГПСЧ сгенерирует значение больше или равно разности одного из интервалов,придётся сравнивать все разности интервалов,то есть если ГПСЧ выдаёт число 17,надо сравнить это число со всеми 3 разностями интеревалов,а это число меньше 31,28,35 а если его прибавить к начальному числу любого из интервалов,65 + 17 = 82,это число находится между 65 и 96,но если ГПСЧ сгенерирует равное число разности придётся уменьшить на 1,то есть по потреблению ресурсов,нужен массив котором будут находится разности интеревалов,чем больше интервалов тем сравнений придётся проводить
    --- Сообщение объединено, 30 сен 2025 ---
    если я правильно понял,битмап это представить в виде последовательности битов
     
  7. R81...

    R81... Active Member

    Публикаций:
    0
    Регистрация:
    1 фев 2020
    Сообщения:
    176
    BT [BitMap], eRx ; - регистр с Rnd
    JC NotQualityRnd
     
  8. MaKsIm

    MaKsIm Active Member

    Публикаций:
    0
    Регистрация:
    11 фев 2008
    Сообщения:
    214
    Можно вычислить X=max(N-N-1) и генерировать Random(X*R), где R количество запрещенных. После просто делим на X и остаток от деления +1 будет смещением от K-го запрещенного числа, где K частное. Для X степени 2 можно обойтись логическими операциями. Если значение совпало со следующими запрещенным(и), тогда просто уменьшаем X на 1 пока X > 0, а при X=0 повторяем генерацию. Или, в случае совпадения, можно сразу повторять генерацию для Random(X).

    Хотя жадный алгоритм в таком подходе может давать сбои (зацикливания).

    Лучше, для современных компьютеров, сделать bitmap - памяти предостаточно. Тогда проверки будут сильно упрощены (сводиться к проверке одного бита). Но вот, в зависимости от диапазона, этот подход с bitmap может и не подойти. Числа больше (128 или 512)×109 вызовут проблемы даже у современных компьютеров. Да и сама инициализация такого блока памяти может быть дольше чем жадный алгоритм.
     
  9. aa_dav

    aa_dav Active Member

    Публикаций:
    0
    Регистрация:
    24 дек 2008
    Сообщения:
    570
    Моя идея заключается в том, чтобы уйти от сравнения со всеми интервалами.
    Мой пример с интервалами (0), 1, 2, (3), 4, 5, 6, 7, (8) где в скобках стоят границы интервалов. Их нельзя выбирать, значит чисел для выбора всего 6: 1, 2, 4, 5, 6, 7.
    Так и будем выбирать сразу только из них построив промежуточный массив и выкидывая рандом от 0 до 5 (индексация массивов с нуля).
    Но выкинув число надо понять в какой оно интервал попало, чтобы пропустить все предыдущие запрещенные числа.
    Если по памяти возможно построить промежуточный массив (что по сложности линейная операция O(N)), то он еще может выглядеть вот так:
    0 -> 1
    1 -> 1
    2 -> 2
    3 -> 2
    4 -> 2
    5 -> 2
    Назовём этот массив A. Это просто немного переформатированный вариант массива что я предлагал выше - вместо абсолютных значений хранятся смещения к ним от текущего индекса.
    Фактически каждая ячейка такого массива соответствует ровно одному "числу из которого можно выбрать" (1, 2, 4, 5, 6, 7) уплотнённому в пространство без промежутков, но каждый элемент содержит сколько ранее в исходном ряде было запрещенных чисел чтобы пропустить их добавив к полученному числу.
    если выпало X=random(0, 5) , то искомое значение есть X + A[ X ]
    Например для X=0 получится 0 + 1 = 1
    Для X=1 получится 1 + 1 = 2
    А для X = 2 получится 2 + 2 = 4
    Мы сразу получаем нужное число без сравнений после предзаготовки промежуточного массива.

    Однако если размеры промежутков очень большие и промежуточный массив занимает слишком много памяти, то придётся уплотнить его информацию в отсортированный массив данных где будут лежать только начала промежутков и их смещения:
    0 -> [ 0, 1 ]
    1 -> [ 2, 2 ]
    (нетрудно заметить, что смещение это тупо индекс + 1, т.е. это всего лишь сводится к отсортированному массиву начала промежутков:
    0 -> 0
    1 -> 2
    Но это верно только если запрещенные числа не могут соседствовать - идти без промежутков - если могут, то надо делать структуру в предыдущем виде, ибо где то смещения будут больше чем +1.
    Задача встаёт теперь в том чтобы максимально быстро в этой структуре данных найти в какой из промежутков мы попали чтобы извлечь смещение единое для всего этого промежутка.
    Если в этом массиве не больше около десятка элементов то из-за кешей быстрее делать линейный поиск.
    Если же речь про гигантские его размеры - есть двоичный поиск который даст к сложности множитель log2(N), а не N.
     
  10. Entropy

    Entropy Member

    Публикаций:
    0
    Регистрация:
    23 авг 2020
    Сообщения:
    200
    aa_dav, если я тебя правильно понял, то нужно получить индекс в промежуточном массиве
    MaKsIm, если я правильно думаю, то что бы сделать bitmap, это таблица степеней двойки, если это попробовать применить к интервалу какому-нибудь, например 65,96 то получается 26= 64 ,27= 128 числа 65 и 96 находятся между 64 и 128
     
  11. Research

    Research Active Member

    Публикаций:
    1
    Регистрация:
    6 янв 2024
    Сообщения:
    313
    Если я правильно понял.

    Для промежуточных массивов получить массив средних чисел - centroids_arr.
    Сгенерировать случаное число в диапазоне от min(centroids_arr) до max(centroids_arr).

    Вычислить к какому из элементов centroids_arr ближе всего сгенерированное random_num.
    Код (Text):
    1. dist[i] = Abs(centroids_arr[i] - random_num).
    В диапазоне где dist(i) минимально, генерировать промежуточное число(для этого сделать отдельную функцию).
     
  12. R81...

    R81... Active Member

    Публикаций:
    0
    Регистрация:
    1 фев 2020
    Сообщения:
    176
    Значение Rnd - это адрес бита в BitMap.
     
  13. MaKsIm

    MaKsIm Active Member

    Публикаций:
    0
    Регистрация:
    11 фев 2008
    Сообщения:
    214
    Если диапазон значений близок к 0, тогда можно не морочиться с оффсетом. Но если надо вычислять в диапазоне +1000000 .. +9999999, тогда можно задать оффсет (+1000000) для проверки и сократить на 256 Кб объем памяти (т.е. длина битмапа будет 8999999 бит или 1125000 байт).
     
  14. aa_dav

    aa_dav Active Member

    Публикаций:
    0
    Регистрация:
    24 дек 2008
    Сообщения:
    570
    Главная идея - выкидывать случайное число только над множеством элементов без граничных.
    Для этого надо как то выкинуть граничные элементы и оставить только полезные.
    Это можно сделать напрямую - взять массив где есть все числа и удалить из него граничные.
    И выкидывать рандом на оставшемся массиве сразу доставая из него номер куда рандом попал. Это максимально быстро.
    А далее я развиваю идею до варианта когда промежуточный массив непомерно большой - чтобы обойтись без него, но не упереться в квадратичную сложность можно снизить сложность до log(N) слегка поменяв что мы храним и как.
     
  15. Entropy

    Entropy Member

    Публикаций:
    0
    Регистрация:
    23 авг 2020
    Сообщения:
    200
    как ты вычислил длину битмапа,что ты перемножил ?
     
  16. MaKsIm

    MaKsIm Active Member

    Публикаций:
    0
    Регистрация:
    11 фев 2008
    Сообщения:
    214
    Ничего не перемножал. Поделил:
    Начало диапазона: 1000000
    Конец диапазона: 9999999
    9999999-1000000=8999999/8=1125000 (округляем до целого в большую сторону т.к. байты занять частично не выйдет)=1125000/1кб=1125кб/1024=1,125Мб
    В прошлый раз обсчитался и сказал 256 Кб, но это от деления на 4, а не на 8. Правильно 128 Кб.
     
  17. Entropy

    Entropy Member

    Публикаций:
    0
    Регистрация:
    23 авг 2020
    Сообщения:
    200
    MaKsIm, я думаю,если bitmap применить к меньшему диапазону,например к 65,96,то есть если эти числа представить в битов 65 = 100 0001b , 96 = 110 0000b оба эти числа 8 бит,если биты считать справа налево то с 0 до 7,если посмотреть на последние 5 бит 96 то это нули,если установить еденицу в промежутки с 5:1 бит числа 65,то получится 69 = 100 0101b это число попадает в диапазон 65,96
     
  18. MaKsIm

    MaKsIm Active Member

    Публикаций:
    0
    Регистрация:
    11 фев 2008
    Сообщения:
    214
    Entropy, а теперь диапазон 63 .. 123. Как в вашем примере получить хотя бы одно четное?
     
  19. Entropy

    Entropy Member

    Публикаций:
    0
    Регистрация:
    23 авг 2020
    Сообщения:
    200
    MaKsIm, согласен,я не прав
     
  20. R81...

    R81... Active Member

    Публикаций:
    0
    Регистрация:
    1 фев 2020
    Сообщения:
    176
    Entropy, у Вас не bitmap,
    а Entropybitmap, в bitmap
    (по классике) каждое число -
    один бит.