Почему Big-O этого алгоритма N ^ 2 * log N

Заполните массив a от [0] до [n-1]: генерируйте случайные числа, пока не получите тот, который еще не был в предыдущих индексах.

Это моя реализация:

public static int[] first(int n) {
    int[] a = new int[n];
    int count = 0;

    while (count != n) {
        boolean isSame = false;
        int rand = r.nextInt(n) + 1;

        for (int i = 0; i < n; i++) {
            if(a[i] == rand) isSame = true;
        }

        if (isSame == false){
            a[count] = rand;
            count++;
        }
    }

    return a;
}

Я думал, что это N ^ 2, но он, по-видимому, N ^ 2logN, и я не уверен, когда рассматривается функция журнала.

Ответы

Ответ 1

Запись 0 заполняется немедленно. Запись 1 имеет вероятность 1 - 1 / n = (n - 1) / n получить заполнение случайным числом. Поэтому нам нужно в среднем n / (n - 1) случайные числа, чтобы заполнить вторую позицию. В общем случае для записи k нам нужны в среднем n / (n - k) случайные числа, и для каждого номера нам нужны сравнения k, чтобы проверить, уникален ли он.

Итак, нам нужно

n * 1/(n - 1) + n * 2/(n - 2) +... + n * (n - 1)/1

сравнения в среднем. Если мы рассмотрим правую половину суммы, мы увидим, что эта половина больше, чем

n * (n/2) * (1/(n/2) + 1/(n/2 - 1) +... + 1/1)

Сумма дробей известна как Θ(log(n)), потому что это гармоническая серия . Итак, вся сумма Ω(n^2*log(n)). Аналогичным образом мы можем показать сумму O(n^2*log(n)). Это означает, что в среднем нам нужно

Θ (n ^ 2 * log (n))

операции.

Ответ 2

Это похоже на проблему Coupon Collector. Вы выбираете из n предметов, пока не получите тот, которого у вас еще нет. В среднем у вас есть попытки O (n log n) (см. Ссылку, анализ не является тривиальным). и в худшем случае вы проверяете n элементов на каждой из этих попыток. Это приводит к средней сложности O (N ^ 2 log N)

Ответ 3

Алгоритм, который у вас есть, не O(n^2 lg n), потому что алгоритм, который у вас есть, может зацикливаться навсегда и не заканчиваться. Представьте себе на первом проходе, вы получите некоторое значение $X $и на каждом последующем проходе, пытаясь получить второе значение, вы продолжаете получать $X $навсегда. В конце концов, мы говорим о худшем случае. Это будет навсегда. Так как ваш худший случай никогда не заканчивается, вы не можете анализировать.

Если вам интересно, если вы знаете, что n всегда является как размером массива, так и верхней границей значений, вы можете просто сделать это:

int[] vals = new int[n];
for(int i = 0; i < n; i++) {
    vals[i] = i;
}
// fischer yates shuffle
for(int i = n-1; i > 0; i--) {
   int idx = rand.nextInt(i + 1);
   int t = vals[idx];
   vals[idx] = vals[i];
   vals[i] = t;
}

Один цикл вниз, один цикл назад. O(n). Простой.

Ответ 4

Если я не ошибаюсь, часть журнала N приходит из этой части:

for(int i = 0; i < count; i++){
    if(a[i] == rand) isSame = true;
}

Обратите внимание, что я изменил n на count, потому что вы знаете, что в каждом цикле у вас есть только теги count.