Есть ли более быстрый способ поиска через накопительное распределение?

1

У меня есть List<Double> который содержит вероятности (веса) для выборки элемента. Например, List содержит 5 значений следующим образом.

0,1, 0,4, 0,2, 0,1, 0,2

Каждое i-е значение Double - это вероятность выборки i-го элемента другого List<Object>.

Как я могу построить алгоритм для выполнения выборки в соответствии с этими вероятностями?

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

0,1, 0,5, 0,7, 0,8, 1,0

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

Random r = new Random();
double p = r.nextDouble();
int total = list.size();
for(int i=0; i < total; i++) {
 double d = list.get(i);
 if(d > p) {
  return i;
 }
}
return total-1;

Этот подход медленный, поскольку я просматриваю список последовательно. На самом деле мой список состоит из 800 000 элементов, связанных с весами (вероятностями), которые мне нужно отбирать. Поэтому, разумеется, этот последовательный подход медленный.

Я не знаю, как может помочь двоичный поиск. Пусть говорят, что я порожден р = 0,01. Затем двоичный поиск может использовать рекурсию следующим образом со списком.

compare 0.01 to 0.7, repeat with L = 0.1, 0.5
compare 0.01 to 0.1, stop 
compare 0.01 to 0.5, stop

0,01 меньше 0,7, 0,5 и 0,1, но я, очевидно, хочу только 0,1. Поэтому критерии остановки для меня все еще не ясны при использовании двоичного поиска.

Если мне нужна библиотека, чтобы помочь в этом, я тоже был бы заинтересован.

  • 0
    Arrays.binarySearch?
  • 0
    Вы используете одни и те же веса несколько раз? Если да, то бинарный поиск поможет, потому что вы можете превратить ваш список индивидуальных весов в кумулятивный список. Это не поможет в создании единственного значения.
Показать ещё 1 комментарий
Теги:
algorithm
random
probability

2 ответа

2
Лучший ответ

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

public static void main (String[] args) {
    double[] cdf = {0.1, 0.5, 0.7, 0.8, 1.0};
    double random = 0.75;  // generate randomly between zero and one
    int el = Arrays.binarySearch(cdf, random);
    if (el < 0) {
        el = -(el + 1);
    }
    System.out.println(el);
}

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

2

Это не самый эффективный с точки зрения памяти подход, но используйте NavigableMap, где ваши значения кумулятивного списка являются ключами. Тогда вы можете просто использовать floorEntry(randon.nextDouble()). Как двоичный поиск, это log (n) пробел и n памяти.

Так...

NavigableMap<Double, Object> pdf = new TreeMap<>();
pdf.put(0.0, "foo");
pdf.put(0.1, "bar");
pdf.put(0.5, "baz");
pdf.put(0.7, "quz");
pdf.put(0.8, "quuz");

Random random = new Random();

pdf.floorEntry(random.nextDouble()).getValue();

Ещё вопросы

Сообщество Overcoder
Наверх
Меню