Почему я всегда получаю случайные результаты независимо от начального значения? - PullRequest
0 голосов
/ 03 октября 2019

У меня довольно сложное приложение Go. Он генерирует много случайных результатов в длинной цепочке. Он засевается только один раз - когда приходит HTTP-запрос.

Независимо от того, что такое начальное число - будь то время Unix или моя собственная буквенно-цифровая функция начального числа - оно всегда генерирует полностьюслучайные результаты.

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

Вот пример (подробный и взятый непосредственно из источника, так как это то, что было запрошено):

func main() {
    sentryDSN := os.Getenv("SENTRY_DSN")

    sentry.Init(sentry.ClientOptions{
        Dsn: sentryDSN,
    })

    sentryHandler := sentryhttp.New(sentryhttp.Options{
        Repanic: true,
    })

    r := chi.NewRouter()

    r.Use(middleware.RequestID)
    r.Use(middleware.RealIP)
    r.Use(middleware.Logger)
    r.Use(middleware.Recoverer)
    r.Use(middleware.URLFormat)
    r.Use(middleware.SetHeader("Content-Type", "application/json"))

    r.Use(middleware.Timeout(60 * time.Second))

    r.Get("/buildingstyle", sentryHandler.HandleFunc(getBuildingStyleRandom))
    r.Get("/buildingstyle/{id}", sentryHandler.HandleFunc(getBuildingStyle))

    r.Get("/character", sentryHandler.HandleFunc(getCharacterRandom))
    r.Get("/character/{id}", sentryHandler.HandleFunc(getCharacter))

    r.Get("/climate", sentryHandler.HandleFunc(getClimateRandom))
    r.Get("/climate/{id}", sentryHandler.HandleFunc(getClimate))

    port := 7531
    fmt.Printf("World Generator API is running on http://localhost:%d.\n", port)
    log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), r))
}

func SeedFromString(source string) error {
    h := md5.New()
    _, err := io.WriteString(h, source)
    if err != nil {
        err = fmt.Errorf("Failed to seed random number generator: %w", err)
        return err
    }
    seed := binary.BigEndian.Uint64(h.Sum(nil))
    rand.Seed(int64(seed))
    return nil
}

func getClimate(w http.ResponseWriter, r *http.Request) {
    id := chi.URLParam(r, "id")

    var o climate.SimplifiedClimate

    err := random.SeedFromString(id)
    if err != nil {
        handleError(w, r, err)
        return
    }

    randomClimate, err := climate.Random()
    if err != nil {
        handleError(w, r, err)
        return
    }
    o = randomClimate.Simplify()

    json.NewEncoder(w).Encode(o)
}

// Generate generates a climate with a given name
func Generate(name string) (Climate, error) {
    rawClimate, err := ByName(name)
    if err != nil {
        err = fmt.Errorf("Could not generate climate by name: %w", err)
        return Climate{}, err
    }
    climate, err := rawClimate.populate()
    if err != nil {
        err = fmt.Errorf("Could not generate climate by name: %w", err)
        return Climate{}, err
    }

    return climate, nil
}

func (climate Climate) populate() (Climate, error) {
    gems := mineral.Gems()
    insects := climate.getFilteredInsects()
    metals := mineral.Metals()

    stones := mineral.Stones()
    trees := climate.getFilteredTrees()

    climate.Seasons = climate.getSeasons()

    lakeChance := rand.Intn(100)
    riverChance := rand.Intn(100)
    oceanChance := rand.Intn(100)
    wetlandsChance := rand.Intn(100)

    if lakeChance > 30 {
        climate.HasLakes = true
    }
    if riverChance > 20 {
        climate.HasRivers = true
    }
    if oceanChance > 80 {
        climate.HasOcean = true
    }
    if wetlandsChance > 80 {
        climate.HasWetlands = true
    }

    soils := climate.getFilteredSoils()

    if climate.HasLakes || climate.HasRivers || climate.HasOcean {
        climate.Fish = climate.getFish()
    } else {
        climate.Fish = []fish.Fish{}
    }

    climate.Insects = insect.RandomSubset(7, insects)
    filteredMetals, err := mineral.RandomWeightedSet(climate.MaxMetals, metals)
    if err != nil {
        err = fmt.Errorf("Could not populate climate: %w", err)
        return Climate{}, err
    }
    climate.Metals = filteredMetals
    climate.Gems = mineral.Random(climate.MaxGems, gems)
    climate.OtherMinerals = mineral.OtherMinerals()

    climate.Animals, err = climate.getAnimals()
    if err != nil {
        err = fmt.Errorf("Could not populate climate: %w", err)
        return Climate{}, err
    }

    climate.Plants, err = climate.getPlants()
    if err != nil {
        err = fmt.Errorf("Could not populate climate: %w", err)
        return Climate{}, err
    }

    climate.Soils = soil.Random(climate.MaxSoils, soils)
    climate.Stones = mineral.Random(climate.MaxStones, stones)
    climate.Trees = tree.RandomSubset(climate.MaxTrees, trees)

    resources := climate.getResources()
    climate.Resources = resources

    description, err := climate.getDescription()
    if err != nil {
        err = fmt.Errorf("Could not populate climate: %w", err)
        return Climate{}, err
    }
    climate.Description = description

    climate.Habitability = climate.calculateHabitability()

    return climate, nil
}

Многие функции, такие как doStuff(), возвращают n случайных элементов из среза с применением некоторой фильтрации.

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

Есть ли какая-то фундаментальная часть операции rand.Intn() или rand.Seed(), о которой я не знаю?

1 Ответ

4 голосов
/ 03 октября 2019

Вы заполняете источник по умолчанию, который используется многими частями системы. В большом, сложном проекте очень вероятно, что есть какая-то другая часть, которая потребляет неопределенное количество случайных значений, зависящих от среды. Возможно даже, что где-то еще в вашем коде у вас есть вызов rand.Seed().

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

Если вы можете воспроизвести это в отдельном фрагменте кода, то мы можем исследовать конкретный случай, но я ожидаю, что когда вы создадите отдельный фрагмент кода, проблема пойдетпрочь, потому что вы удалили другого потребителя (ей).

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