Предположим, у нас есть массив с целыми числами от -N до N в массиве размером 2N + 1. Сначала мы перемешиваем элементы в массиве, затем пытаемся найти максимальное целое число, перебирая массив от первого элемента до последнего элемента: (пример кода на Java)

int called = 0;
int max = Integer.MIN_VALUE;
for (int i : array) {
    if (i > max) {
        called++;
        max = i;
    }
}

Каково ожидание (среднее за многие прогоны) значения called после итерации по массиву?

Редактировать:

Как я обнаружил, что это близко к ln (array.length):

public static void main(String args[]) {
    List<Integer> list = new ArrayList<>();
    for (int i = 0; i < 1000000; i++) list.add(i);

    int called = 0;
    int runs = 100;

    for (int i = 1; i <= runs; i++) {
        Collections.shuffle(list);
        int max = -1;
        for (int num : list) {
            if (num > max) {
                called++;
                max = num;
            }
        }
    }
    System.out.println("Expectation: " + called/runs);
}
2
wz366 14 Мар 2018 в 02:43

2 ответа

Лучший ответ

Рассмотрим случайно перемешанный массив. Мы хотим оценить K - во сколько раз i-й элемент больше всех своих предшественников.

Ожидаемое значение K равно сумме вероятностей того, что i-й элемент больше всех своих предшественников. E (K) = Σ 1 2N + 1 P i .

Теперь мы хотим найти P i . Рассмотрим префикс длины i для нашего массива. Вероятность того, что последний элемент префикса будет самым большим, равна 1 / i. Итак, мы имеем P i = 1 / i.

Следовательно: E (K) = Σ 1 2N + 1 1 / i. Мы можем оценить эту сумму через определенный интеграл как ln (2N + 1) + O (1).

Итак, ответ - ln (2N + 1) + O (1).

И моделирование Монте-Карло в качестве доказательства:

constexpr size_t N = 1000;
std::array<int, 2 * N + 1> arr;
std::iota(arr.begin(), arr.end(), -N);
std::random_device rd;
std::mt19937 g(rd());
double moving = 0;
for (double trial = 1; trial < 10001; ++trial) {
    std::shuffle(arr.begin(), arr.end(), g);
    int called = 0;
    int max = std::numeric_limits<int>::min();
    for (int i = 1; i < arr.size(); ++i) {
        if (arr[i] > max) {
            ++called;
            max = arr[i];
        }
    }
    if (trial > 1) {
        moving = moving * ((trial - 1) / trial) + called / trial;
    }
    else {
        moving = called;
    }
}
cout << "actual:   " << moving << endl;
cout << "expected: " << std::log(2 * N + 1) << " + O(1)" << endl;
actual:   8.1581   
expected: 7.6014 + O(1)
3
Yola 15 Мар 2018 в 08:04

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

Обозначение цикла

Числа Стирлинга первого рода

Как указали Йола и Ричи, ответ должен быть ln (n) + γ (где γ - это константа Эйлера-Маскерони, а n - количество элементов в массиве).

0
wz366 15 Мар 2018 в 19:55