Быстрое добавление случайных величин в С++

Краткая версия: как наиболее эффективно представлять и добавлять две случайные величины, заданные списками их реализаций?

Немного более длинная версия: для рабочего проекта мне нужно добавить несколько случайных величин, каждый из которых задается списком значений. Например, реализация rand. вар. A являются {1,2,3}, а реализации B - {5,6,7}. Следовательно, мне нужно распределение А + В, т.е. {1 + 5,1 + 6,1 + 7,2 + 5,2 + 6,2 + 7,3 + 5,3 + 6,3 + 7 }. И мне нужно сделать это несколько раз (пусть обозначить это количество дополнений как COUNT, где COUNT может достигать 720) для разных случайных величин (C, D,...).

Проблема:, если я использую этот глупый алгоритм суммирования каждой реализации A с каждой реализацией B, сложность экспоненциальна в COUNT. Следовательно, для случая, когда каждый r.v. задается тремя значениями, сумма вычислений для COUNT = 720 составляет 3 ^ 720 ~ 3,36xe ^ 343, которая продлится до конца наших дней, чтобы рассчитать:) Не говоря уже о том, что в реальной жизни длина каждого р.в. будет 5000 +.

Решения: Первое решение состоит в том, чтобы использовать тот факт, что я в порядке с округлением, т.е. С целыми значениями реализаций. Таким образом, я могу представлять каждый r.v. как вектор, а для индекса, соответствующего реализации, у меня есть значение 1 (когда r.v. имеет эту реализацию один раз). Так что для r.v. A и вектора реализаций, индексированных от 0 до 10, вектор, представляющий A, будет [0,1,1,1,0,0,0...], а представление для B будет [0,0,0, 0,0,1,1,1,0,0,10]. Теперь я создаю A + B, перейдя через эти векторы и делаю то же самое, что и выше (суммируйте каждую реализацию A с каждой реализацией B и кодифицируйте ее в одну и ту же векторную структуру, квадратичную сложность в векторной длине). Поверхность этого подхода заключается в том, что сложность связана. Проблема такого подхода заключается в том, что в реальных приложениях реализации A будут находиться в интервале [-50000,50000] с гранулярностью 1. Следовательно, после добавления двух случайных величин диапазон A + B достигает -100K, 100K.. и после 720 дополнений, диапазон SUM (A, B,...) доходит до [-36M, 36M] и даже квадратичной сложности (по сравнению с экспоненциальной сложностью) на массивах, которые это большое займет навсегда.

2/Чтобы иметь более короткие массивы, можно было бы использовать хэш-карту, которая, скорее всего, уменьшит количество операций (обращения к массиву), участвующих в + B, поскольку предполагается, что какая-то нетривиальная часть теоретического диапазона [ -50K, 50K] никогда не будет реализацией. Однако при непрерывном суммировании все более и более случайных величин число реализаций возрастает экспоненциально, в то время как диапазон увеличивается только линейно, поэтому плотность чисел в промежутке возрастает с течением времени. И это убьет преимущества hashmap.

Итак, вопрос: как я могу эффективно справиться с этой проблемой? Решение необходимо для вычисления VaR при торговле электроэнергией, где все распределения задаются эмпирически и не похожи на обычные распределения, поэтому формулы бесполезны, мы можем имитировать только.


Использование математики рассматривалось как первый вариант как половина нашего отдела. являются математиками. Однако дистрибутивы, которые мы собираемся добавить, плохо себя ведут, а COUNT = 720 - крайняя. Скорее всего, мы будем использовать COUNT = 24 для ежедневного VaR. Принимая во внимание плохое поведение распределений для добавления, для COUNT = 24 центральная предельная теорема не будет слишком тесно (дистрибутив SUM (A1, A2,..., A24) не будет близким к нормальному). Поскольку мы рассчитываем возможные риски, мы хотели бы получить максимально точное число.

Предполагаемое использование: у вас есть часовые сбои от некоторой операции. Распределение денежных потоков в течение одного часа - r.v. A. В течение следующего часа, он r.v. B и т.д. И ваш вопрос: какая самая большая потеря в 99 процентах случаев? Таким образом, вы моделируете денежные потоки за каждый из этих 24 часов и добавляете эти денежные потоки в качестве случайных величин, чтобы получить распределение общего количества casfhlow за весь день. Затем вы берете 0,01 квантиль.

Ответ 1

В основном существуют два метода. Аппроксимативный и точный...

Аппроксимативный метод моделирует сумму случайных величин множеством выборок. В принципе, имея случайные переменные A, B, мы случайным образом выбираем из каждого r.v. 50K раз, добавьте выборочные значения (здесь SSE может многое помочь), и у нас есть распределение A+B. Так математики сделали бы это в Mathematica.

Точный метод использует то, что предложил Дэн Пузи, а именно суммируя только небольшую часть каждого r.v. плотность. Пусть говорят, что у нас есть случайные переменные со следующими "плотностями" (где каждое значение имеет одинаковое правдоподобие для простоты)

A = {-5,-3,-2}
B = {+0,+1,+2}
C = {+7,+8,+9}

Сумма A+B+C будет

{2,3,3,4,4,4,4,5,5,5,5,5,6,6,6,6,6,6,7,7,7,7,7,8,8,8,9}

и если я хочу точно знать весь дистрибутив, у меня нет другого выбора, кроме суммирования каждого элемента A с каждым элементом B, а затем каждый элемент этой суммы с каждым элементом C. Однако, если я хочу только 99% VaR этой суммы, т.е. 1% процентиля этой суммы, мне нужно суммировать наименьшие элементы A,B,C.

Точнее, я возьму nA,nB,nC наименьшие элементы из каждого дистрибутива. Чтобы определить nA,nB,nC, сначала установите их в 1. Затем увеличьте nA на единицу, если A[nA] = min( A[nA], B[nB], C[nC]) (считая, что A,B,C отсортировано). Таким образом, я могу получить nA, nB, nC наименьшие элементы A,B,C, которые мне придется суммировать (каждый друг с другом) и взять X-ю наименьшую сумму (где X равно 1%, умноженное на общее количество комбинаций сумм, т.е. 3 * 3 * 3 для A,B,C). Это также говорит о том, когда нужно остановить увеличение nA,nB,nC - остановить, когда nA*nB*nC > X.

Однако, как и я, я снова делаю ту же избыточность, то есть вычисляю все распределение A+B+C слева от 1% процентиля. Однако это будет намного короче, чем вычисление всего дистрибутива A+B+C. Но я считаю, что должен быть простой итеративный алгоритм, чтобы сказать, что данный номер VaR в O(a*b), где A - количество добавленных r.v.s и B - максимальное количество элементов в плотности каждого r.v.

Буду рад за любые комментарии относительно того, насколько я прав.

Ответ 2

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

Я не думаю, что вы можете сократить общее количество дополнений.

Кроме того, вы должны изучить параллельные алгоритмы и многопоточность, если это применимо.

В этот момент большинство процессоров могут выполнять дополнения параллельно, учитывая правильные инструкции (SSE), что будет делать добавления много раз быстрее (все еще не вылечить проблему сложности).

Ответ 3

Как вы сказали в своем вопросе, вам понадобится очень много вычислений, чтобы получить точный ответ. Так что этого не произойдет.

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

Я предполагаю, что есть следствие, и для чего используются результаты? Знание того, как используются результаты, будет информировать решение о том, как создаются результаты.

Ответ 4

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

Если мы определяем четыре группы W, X, Y и Z, каждая из которых имеет три элемента, по вашей собственной математике это приводит к большому количеству операций:

  • W + X = > 9 операций
  • (W + X) + Y = > 27 операций
  • (W + X + Y) + Z = > 81 операции
  • ВСЕГО: 117 операций

Однако, если мы предположим строго упорядоченное определение вашей операции "добавить", чтобы два набора {a,b} и {c,d} всегда приводили к {a+c,a+d,b+c,b+d}, тогда ваша операция ассоциативный. Это означает, что вы можете сделать это:

  • W + X = > 9 операций
  • Y + Z = > 9 операций
  • (W + X) + (Y + Z) = > 81 операции
  • ВСЕГО: 99 операций

Это экономия 18 операций для простого случая. Если вы распространите вышеуказанное на 6 групп из 3 членов, общее количество операций может быть уменьшено с 1089 до 837 - почти на 20% экономии. Это улучшение более выражено, чем больше данных у вас (больше наборов или больше элементов даст больше сбережений).

Кроме того, это открывает проблему для лучшей параллелизации: если у вас есть 200 групп для обработки, вы можете начать с объединения 100 пар параллельно, затем 50 пар или результатов, затем 25 и т.д. Это позволит в значительной степени из parallelism, что должно дать вам гораздо лучшую производительность. (Например, 720 наборов будут добавлены в ~ 10 параллельных операций, так как каждый параллельный add позволит увеличить COUNT в 2 раза.)

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

РЕДАКТИРОВАТЬ: Если ваш реальный вопрос - это "какая самая большая потеря", тогда это гораздо более простая проблема. Учитывая, что каждое значение в конечном наборе представляет собой сумму одного значения из каждого набора компонентов, ваша самая большая потеря обычно будет найдена путем объединения самого низкого значения из каждого набора компонентов. Поиск этих более низких значений (по одному значению для каждого набора) является гораздо более простым заданием, и тогда вам требуется только сумма, ограниченная набором значений.