Что не так в решении голанга задачи литкода?

Задача LeetCode «380. Вставить Удалить GetRandom O(1)»

Задача

Implement the RandomizedSet class:

RandomizedSet() Initializes the RandomizedSet object.
bool insert(int val) Inserts an item val into the set if not present. Returns true if the item was not present, false otherwise.
bool remove(int val) Removes an item val from the set if present. Returns true if the item was present, false otherwise.
int getRandom() Returns a random element from the current set of elements (it's guaranteed that at least one element exists when this method is called). Each element must have the same probability of being returned.
You must implement the functions of the class such that each function works in average O(1) time complexity.

Первое, что приходит на ум — использовать карту, но тогда возникает вопрос, как сделать GetRandom в O(1). Общее решение — хранить карту и массив/вектор/срез, карту — для вставки, удаления, массив/вектор/срез — для выборки случайного элемента. Так как я пишу на Golang, а там итератор обхода карты начинается со случайного элемента, я решил не усложнять и просто вернуть из итератора первый элемент. И это работает. Но LeetCode не считает это решение правильным. Я удивился, посмотрел другие решения. LeetCode принимает решения даже со сложностью O(n), но не мое

Мой код

type RandomizedSet struct {
    items map[int]struct{}
}

func Constructor() RandomizedSet {
    return RandomizedSet{
        items: make(map[int]struct{}),
    }
}

func (this *RandomizedSet) Insert(val int) bool {
    if _, ok := this.items[val]; ok {
        return false
    }
    this.items[val] = struct{}{}
    return true
}

func (this *RandomizedSet) Remove(val int) bool {
    if _, ok := this.items[val]; !ok {
        return false
    }
    delete(this.items, val)
    return true
}

func (this *RandomizedSet) GetRandom() int {
    for k := range this.items {
        return k
    }
    return -1
}

Leetcode его не принимает. Но он принимает, если GetRandom имеет следующую форму:

func (this *RandomizedSet) GetRandom() int {
    n := len(this.items)
    randNum := rand.Intn(n)
    var counter int

    for k := range this.items {
        if counter == randNum {
            return k
        }
        counter++
    }

    return -1
}

Может я чего-то не понимаю? Кто прав — я или LeetCode?

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

Burak Serdar 04.09.2024 16:24

почему не случайно? В исходниках go (runtime/map_noswiss.go) в func mapiterinit есть инициализация итератора случайными значениями: r := uintptr(rand()) it.startBucket = r & BucketMask(h.B) it.offset = uint8(r > > h.B & (abi.OldMapBucketCount - 1))

bam 04.09.2024 16:37

Вы не можете полагаться на порядок итерации карты ни в чем, включая случайность порядка.

JimB 04.09.2024 16:39

Я не полагаюсь на случайность полного обхода карты. Мне нужен только 1 случайный элемент. А если посмотреть исходные тексты карты (runtime/map_noswiss.go), то итератор начинается со случайного элемента. В чем дело?

bam 04.09.2024 16:43

Неважно, проходите ли вы по одному ключу или по всей карте, вы пытаетесь полагаться на детали реализации, которые не удовлетворяют вашим требованиям, и даже если это так, не гарантируется стабильность между версиями. «Случайность» предназначена только для того, чтобы эффективно устранить непреднамеренную зависимость от порядка итераций и не более того. Проведение простого теста показывает, что ключи возвращаются первыми с некоторой предвзятостью go.dev/play/p/weQhvwwtJ0q

JimB 04.09.2024 16:52
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
API ввода вопросов - это полезный инструмент для интеграции моделей машинного обучения, таких как ChatGPT, в приложения, требующие обработки...
2
5
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ваш случайный код зависит от итерации, возвращающей каждый элемент на карте с более или менее одинаковой вероятностью. Итерация рандомизирована, но для начала она выбирает случайный сегмент (не случайный элемент). Если в одном сегменте карты находится несколько элементов, второй и последующие никогда не будут первыми в итерации. Вы можете увидеть эффект здесь, где я создаю карту с ключами 0...99, а затем запускаю 100 000 итераций по ключам, чтобы найти первый возвращенный элемент. Обычно никогда не выбираются 6 или 7 значений. См. ссылку на игровую площадку

package main

import "fmt"

const N = 100

func main() {
    m := map[int]struct{}{}
    for i := range N {
        m[i] = struct{}{}
    }
    first := map[int]bool{}
    for range N * 1000 {
        for k := range m {
            first[k] = true
            break
        }
    }
    for i := range N {
        if !first[i] {
            fmt.Println(i)
        }
    }
}

Спасибо. В то же время я обнаружил форму «for i := range <CONST> {...}» и «for range <CONST> {...}». Совсем for писать неудобно, а после Kotlin я пропустил "repeat(N) { ... }" и "for i in 0..N { ...}". Теперь я нашел замену.

bam 04.09.2024 17:07

Другие вопросы по теме