Правильный спавнинг рандомных вещей

Версия Minecraft
1.7.10
214
11
59
Привет.

Я работаю над модом, который должен спавнить предметы в 5000 сундуках по всей карте.

В коде использую нехитрую логику:
Каждый предмет хранит в себе шанс спавна - число от 0 до 100, присвоенное при регистрации.
Далее, при спавне, я для каждого предмета беру число rand, и если оно меньше чем шанс у предмета, то он становится кандидатом на спавн.
Java:
int rand = new Random(System.currentTimeMillis()).nextInt(100);
Если на выходе предметов несколько, то я просто получаю еще одно число в границах количества этих предметов и спавню предмет с этим индексом в массиве.
Java:
int randList = new Random(System.currentTimeMillis()).nextInt(list.size());

И вот возникла проблема. Очень часто спавнятся одни и те же вещи. Так, возможно, что в 10 сундуках рядом заспавнится один и тот же предмет :(
Может есть толковые люди, подсказавшие либо другой алгоритм спавнинга, либо что-то с использованием Random в java для таких целей ?
 
214
11
59
Пока есть идея только делать промежуточный массив со всеми предметами, затем перемешивать их и спавнить по сундукам. Но это не исключает того, что большая часть всех этих предметов не будет одним и тем же :(
 
214
11
59
Смотри в сторону WeightedRandom и изучай как он используется в обычных данжах майна.

Так ?

Java:
public class asd {

    class Item {
        public int weight;
    }

    private Item wr(Item[] items) {
        int totalWeight = 0;
        for (Item i : items) {
            totalWeight += i.weight;
        }
        int randomIndex = -1;
        int random = new Random(System.currentTimeMillis()).nextInt(totalWeight);
        for (int i = 0; i < items.length; ++i) {
            random -= items[i].weight;
            if (random <= 0) {
                randomIndex = i;
                break;
            }
        }
        return items[randomIndex];
    }
}
 
3,005
192
592
Вынеси рандом в отдельную переменную, а не создавай новый.
Скорее всего у тебя рандом создается в один и тот же "currentTimeMillis" и из-за этого рандомные цифры одинаковые.
 
3,005
192
592
Знаю "кодера", который делал каждый раз new Random().nextInt(count); и горел, почему же у него всегда одни числа....
 
3,005
192
592
Ну, видимо потому что он создавать несколько вещей в одно время...
 
214
11
59
А они не будут одни и те же, там в конструкторе передается текущее время, а оно всегда разное.
Я давненько читал, что если в цикле выписывать значения времени по currentTimeMillis, то можно заметить, что иногда время не изменяется между итерациями. Причиной там было что-то вроде гранулярности времени, но это я с трудом помню.

В общем, выше приведенный код + статический рандом пока дают мне хоть какое-то, но разнообразие спавна. Продолжаю тестить... :coffee:
 
2,505
81
397
Вставлю свои 5 копеек.

Лучше не рассчитывать общий вес при каждой попытке зарандомить. Ты можешь сделать это после регистрации всех айтемов (или в пост ините, например). А чтобы не сохранять нигде этот общий вес, нужно нормализовать веса всех айтемов (поделить каждый вес на максимальный вес). Тогда ты сможешь рандомить не nextInt(totalWeight), а просто nextDouble().

Следующий шаг оптимизации это превратить линейный поиск в бинарный. Но для этого тебе нужно при нормализации расположить вероятности последовательно. Т.е. из такого вида [0.1, 0.5, 0.4] превратить в такой [0.1, 0.6, 1.0].

Тестится все это очень просто. Создай Map<Item, Integer> и 1000000000 раз зарандом айтем, добавляя единичку в мапу.
 
214
11
59
Может кто еще подскажет как лучше быть, если у меня 10 предметов имеют шанс 0.2, алгоритм ведь всегда будет находить последний элемент в списке при поиске. У меня пока из идей только брать все эти предметы и выбирать nextInt элемент из них
 
214
11
59
Вопрос закрыт. Добился такого результата, который меня устраивает

totalWeight:3.45
input:
0.2
0.1
0.5
0.9
0.6
0.5
0.11
0.54
sorted:
0.9
0.6
0.54
0.5
0.5
0.2
0.11
0.1
Result:
0.5 was returned 25
0.6 was returned 8
0.9 was returned 50
0.2 was returned 1
0.1 was returned 1
0.54 was returned 7
0.11 was returned 1

Process finished with exit code 0
 
214
11
59
Иначе не получается. Лучший вариант для меня - весовой рандом. С бинарным поиском у меня чутка не срослось, а обычный рандом вообще непригоден здесь
 
Сверху