Линейный убывающий рандом

Полезная функция?

  • Да.

    Голосов: 9 60.0%
  • Нет.

    Голосов: 1 6.7%
  • Есть проще и/или быстрее.

    Голосов: 4 26.7%
  • Не понял, зачем она нужна.

    Голосов: 1 6.7%

  • Всего проголосовало
    15
215
1
20
Решил тут одевать зомби в случайный шмот из массива, сначала сделал выборку через MathHelper.getRandomIntegerInRange(random, min, max), потом поразмыслил и пришел к выводу, что хочу неравномерный рандом, убывающий вверх, чтобы, например, крутой алмазный шмот выпадал реже, чем какой-нибудь кожаный). После некоторых поисков - проблема была в том, что я даже не смог сформулировать строку для поиска (либо гугл просто меня не понял) - решил написать собственную функцию. И вот что у меня получилось:

Код:
/** ## Линейный убывающий рандом ##
 * @param minNumber - наименьший номер выборки
 * @param maxNumber - наибольший номер выборки (> minNumber)
 * @param minWeight - минимально возможная вероятность  (> 0)
 * @param maxWeight - максимально возможная вероятность (> minWeight)
 * @param rand      - передача рандома извне на случай привязки к сиду мира, например
 * @return - случайное число из диапазона
 */
 public static int decreasingRandom(int minNumber, int maxNumber, double minWeight, double maxWeight, Random rand){ // для значений: 0, 9, 0.2, 1.0 
 if(maxNumber <= minNumber){
 Console.out().println("[DecreasingRandom] Error: maxNumber (" + maxNumber + ") must be > that minNumber (" + minNumber + ").");
 return minNumber; 
 }
 if(minWeight <= 0){
 Console.out().println("[DecreasingRandom] Error: minWeight (" + minWeight + ") must be > 0.");
 return minNumber; 
 }
 if(maxWeight <= minWeight){
 Console.out().println("[DecreasingRandom] Error: maxWeight (" + maxWeight + ") must be > minWeight (" + minWeight + ").");
 return minNumber; 
 }
 int count = 1 + maxNumber - minNumber; // всего чисел
 double[] chances = new double [count]; // массив вероятностей для чисел
 int[] numbers = new int[count]; // массив возможных чисел
 double step = (maxWeight - minWeight) / (count - 1); // шаг (разница вероятности между соседними числами) = размер диапазона / количество чисел - 1
 double sum = 0D; // общая масса вероятностей для всех чисел
 for(int i = 0; i < count; i++){
 numbers[i] = minNumber + i; // заполняем массив возможных чисел значениями из заданного диапазона
 chances[i] = maxWeight - (step * i); // заполняем массив убывающей последовательностью вероятностей с шагом step
 sum += chances[i]; // складываем все вероятности всех чисел
 } 

 double choose = rand.nextDouble() * sum; // вычисляем случайное число из общей суммы вероятностей
 sum = 0D; // сбрасываем сумму вероятностей
 for(int i = 0; i < count; i++){
 sum += chances[i]; // снова складываем последовательно вероятности всех чисел
 if(sum >= choose){
 return numbers[i]; // как только найденное случайно число становится меньше - найден номер результата, возвращаем его
 } 
 }
 // итог (если расчеты не приведут к результату, выдаем ошибку в консоль и минимальный из возможных результатов) 0
 Console.out().println("[DecreasingRandom] Error: Function doesn't make a choice correctly. Check it.");
 return minNumber; 
 }
Возможности:
1. Можно задать любой диапазон (int) чисел (включая отрицательные).
2. Можно задать любые (double) массы пределов вероятности.
3. Предусмотрена защита от ошибок в строке вызова функции для облегчения выявления ошибок.
4. Можно использовать зерно мира.
Уверен, что я изобрел велосипед, что этот велосипед - инвалид, что есть более элегантные способы достичь желаемого результата, но мне эти способы неизвестны. Буду рад, если кому-то моя функция окажется полезной.
 
Почитай учебник по математике. Сумма большого количества случайных величин имеет закон распределения близкий к нормальному (гауссовскому).
Это доказывает центральная предельная теорема.
Не совсем понял для чего это все нужно, можно же просто какую-нибудь функцию, например экспоненту, от обычного рандома брать.
 
Да я в математике ни бум-бум, смысла нет читать учебник.
Для чего всё это нужно? Для того, чтобы одни предложили свои готовые рабочие варианты (более простые? более короткие? более эффективные? более быстрые?), а другие при надобности могли прийти и этими вариантами воспользоваться.
 
А можешь показать, как ты используешь свой метод дальше?
Слишком много у тебя каких-то вычислений. Сложный алгоритм.
 
Да, могу. Только осторожно, глаза не сломайте - я, как гинеколог, всё делаю через одно место. И местами бывает много комментариев, так как мод большой, боюсь, что если зайду в какой-то кусок кода через полгода - вообще не вспомню, что зачем писал.
Раз:
Код:
 /**
 * Одеваем свинозомби
 * @param entity - свинозомби
 * @param maxLevel - максимальный уровень защиты
 * @param clothChance - шанс одевания брони
 * @param dropChance - шанс выпадения брони
 */
 public static void getPigZombieArmor(EntityLiving entity, int maxLevel, double clothChance, float dropChance, double minweight, double maxweight) {
 Random rand = new Random(System.currentTimeMillis());
 if(rand.nextDouble() > clothChance){return;}
 int size = 15;
 Item[][] clothes = new Item[size][4];
 clothes[0][0] = RHArmors.paperHelmet; clothes[0][1] = RHArmors.paperChestplate; clothes[0][2] = RHArmors.paperLeggings; clothes[0][3] = RHArmors.paperBoots; 
 clothes[1][0] = RHArmors.pajamaHelmet; clothes[1][1] = RHArmors.pajamaChestplate; clothes[1][2] = RHArmors.pajamaLeggings; clothes[1][3] = RHArmors.pajamaBoots;
 clothes[2][0] = RHArmors.pajamaFemaleHelmet;clothes[2][1] = RHArmors.pajamaFemaleChestplate;clothes[2][2] = RHArmors.pajamaFemaleLeggings; clothes[2][3] = RHArmors.pajamaFemaleBoots;
 clothes[3][0] = RHArmors.pajamaMaleHelmet; clothes[3][1] = RHArmors.pajamaMaleChestplate; clothes[3][2] = RHArmors.pajamaMaleLeggings; clothes[3][3] = RHArmors.pajamaMaleBoots;
 clothes[4][0] = RHArmors.caneHelmet; clothes[4][1] = RHArmors.caneChestplate; clothes[4][2] = RHArmors.caneLeggings; clothes[4][3] = RHArmors.caneBoots;
 clothes[5][0] = RHArmors.vineHelmet; clothes[5][1] = RHArmors.vineChestplate; clothes[5][2] = RHArmors.vineLeggings; clothes[5][3] = RHArmors.vineBoots;
 clothes[6][0] = RHArmors.flaxenHelmet; clothes[6][1] = RHArmors.flaxenChestplate; clothes[6][2] = RHArmors.flaxenLeggings; clothes[6][3] = RHArmors.flaxenBoots;
 clothes[7][0] = RHArmors.cactusHelmet; clothes[7][1] = RHArmors.cactusChestplate; clothes[7][2] = RHArmors.cactusLeggings; clothes[7][3] = RHArmors.cactusBoots;
 clothes[8][0] = RHArmors.lilyHelmet; clothes[8][1] = RHArmors.lilyChestplate; clothes[8][2] = RHArmors.lilyLeggings; clothes[8][3] = RHArmors.lilyBoots;
 clothes[9][0] = RHArmors.furHelmet; clothes[9][1] = RHArmors.furChestplate; clothes[9][2] = RHArmors.furLeggings; clothes[9][3] = RHArmors.furBoots;
 clothes[10][0] = RHArmors.hideHelmet; clothes[10][1] = RHArmors.hideChestplate; clothes[10][2] = RHArmors.hideLeggings; clothes[10][3] = RHArmors.hideBoots; 
 clothes[11][0] = RHArmors.jeansHelmet; clothes[11][1] = RHArmors.jeansChestplate; clothes[11][2] = RHArmors.jeansLeggings; clothes[11][3] = RHArmors.jeansBoots; 
 clothes[12][0] = RHArmors.boiledHelmet; clothes[12][1] = RHArmors.boiledChestplate; clothes[12][2] = RHArmors.boiledLeggings; clothes[12][3] = RHArmors.boiledBoots; 
 clothes[13][0] = RHArmors.ninjaHelmet; clothes[13][1] = RHArmors.ninjaChestplate; clothes[13][2] = RHArmors.ninjaLeggings; clothes[13][3] = RHArmors.ninjaBoots; 
 clothes[14][0] = RHArmors.soldierHelmet; clothes[14][1] = RHArmors.soldierChestplate; clothes[14][2] = RHArmors.soldierLeggings; clothes[14][3] = RHArmors.soldierBoots;
 
 // выбираем случайный комплект брони из созданного массива
 int number = Functions.decreasingRandom(0, size - 1, minweight, maxweight, rand); 
 // создаем массив стаков из выбранного комплекта брони
 ItemStack[] armor = {new ItemStack(clothes[number][0]), new ItemStack(clothes[number][1]), new ItemStack(clothes[number][2]), new ItemStack(clothes[number][3])};
 // надеваем броню, задаем ей шанс выпадения
 entity.setCurrentItemOrArmor(4, armor[0]);
 entity.setCurrentItemOrArmor(3, armor[1]);
 entity.setCurrentItemOrArmor(2, armor[2]);
 entity.setCurrentItemOrArmor(1, armor[3]);
 for(int i = 1; i < 5; i++){entity.setEquipmentDropChance(i, dropChance * (size - number));}
 }

Два:
Код:
 /**
 * Выдаем оружие хостилийцам, техлизорам, зомби
 * @param entity - существо
 * @param type - 1 - зомби, 2 - хостилиец, 3 - хостилийский воин, 4 - хостилийский старшина, 5 - техлизор
 * @param dropChance - шанс выпадения оружия
 * @return случайное оружие из списка
 */
 public static ItemStack getMeleeWeapon(EntityLiving entity, int type, float dropChance, double minWeight, double maxWeight){
 ItemStack weapon = new ItemStack(Items.wooden_pickaxe);
 Random rand = new Random(System.currentTimeMillis());
 List <Item> swords = new ArrayList<Item>();
 
 switch (type){
 case 1:{ // ZOMBIE
 swords.add(Items.wooden_axe);
 swords.add(Items.wooden_sword);
 swords.add(Items.stone_axe);
 swords.add(Items.stone_sword);
 swords.add(Items.golden_axe);
 swords.add(Items.golden_sword);
 swords.add(RHTools.lapisAxe);
 swords.add(RHTools.lapisSword);
 swords.add(RHTools.rubyAxe);
 swords.add(RHTools.rubySword);
 swords.add(RHTools.obsidianAxe);
 swords.add(RHTools.obsidianSword);
 swords.add(RHTools.boneAxe);
 swords.add(RHTools.boneSword);
 swords.add(Items.iron_axe);
 swords.add(Items.iron_sword);
 swords.add(RHTools.bronzeAxe);
 swords.add(RHTools.bronzeSword);
 swords.add(RHTools.scorpdirtAxe);
 swords.add(RHTools.scorpdirtSword);
 swords.add(RHTools.scorpnetherAxe);
 swords.add(RHTools.scorpnetherSword);
 swords.add(RHTools.scorpcaveAxe);
 swords.add(RHTools.scorpcaveSword);
 swords.add(RHTools.scorpiceAxe);
 swords.add(RHTools.scorpiceSword);
 }
 case 2:{ // HOSTILIAN
 swords.add(Items.wooden_axe);
 swords.add(Items.wooden_sword);
 swords.add(Items.stone_axe);
 swords.add(Items.stone_sword);
 swords.add(Items.golden_axe);
 swords.add(Items.golden_sword);
 swords.add(RHTools.lapisAxe);
 swords.add(RHTools.lapisSword);
 swords.add(RHTools.rubyAxe);
 swords.add(RHTools.rubySword);
 break;
 }
 case 3:{ // HOSTILIAN PRIVATE
 swords.add(RHTools.obsidianAxe);
 swords.add(RHTools.obsidianSword);
 swords.add(RHTools.boneAxe);
 swords.add(RHTools.boneSword);
 swords.add(Items.iron_axe);
 swords.add(Items.iron_sword);
 swords.add(RHTools.bronzeAxe);
 swords.add(RHTools.bronzeSword);
 break;
 }
 case 4:{ // HOSTILIAN MAJOR
 swords.add(RHTools.scorpdirtAxe);
 swords.add(RHTools.scorpdirtSword);
 swords.add(RHTools.scorpnetherAxe);
 swords.add(RHTools.scorpnetherSword);
 swords.add(RHTools.scorpcaveAxe);
 swords.add(RHTools.scorpcaveSword);
 swords.add(RHTools.scorpiceAxe);
 swords.add(RHTools.scorpiceSword);
 break;
 }
 case 5:{ // TEHLIZOR
 swords.add(Items.diamond_axe);
 swords.add(Items.diamond_sword);
 swords.add(RHTools.hostiliteAxe);
 swords.add(RHTools.hostiliteSword);
 swords.add(RHTools.emeraldAxe);
 swords.add(RHTools.emeraldSword);
 swords.add(RHTools.witherskeletonAxe);
 swords.add(RHTools.witherskeletonSword);
 swords.add(RHTools.dragonAxe);
 swords.add(RHTools.dragonSword);
 swords.add(RHTools.witherAxe);
 swords.add(RHTools.witherSword);
 break;
 }

 default: return weapon;
 }
 int number = Functions.decreasingRandom(0, swords.size() - 1, minWeight, maxWeight, rand);
 weapon = new ItemStack(swords.get(number));
 int level = MathHelper.getRandomIntegerInRange(rand, 0, type);
 if(level > 0){weapon.addEnchantment(Enchantment.sharpness, level);}
 entity.setCurrentItemOrArmor(0, weapon);
 entity.setEquipmentDropChance(0, dropChance * (swords.size() - number));
 return weapon;
 }
 
Что-то слишком сложное.
Кстати, не стоит каждый раз создавать массивы. Это слииишком непроизводительно. И не стоит использовать add много раз. Лучше проинициализировать все сразу. Это во много раз быстрее.
Код:
// А еще, можно вот так иницализировать массивы. Это красивее.
Item[][] items = new Item[][] {
    {Items.apple, Items.arrow},
    {Items.apple, Items.arrow},
};

// Это вместо того, чтобы использовать add много раз.
ArrayList<Item> listItems = new ArrayList<Item>(Arrays.asList(items[0]));


А что-ты хочешь вообще? "чтобы, например, крутой алмазный шмот выпадал реже, чем какой-нибудь кожаный)" вообще никак не связано с "Линейный убывающий рандом". Тебе нужно что-то вроде моделирования дискретного случайного числа. Т.е. Создаешь где-нибудь массив пар: итем, вероятность. Приводишь сумму вероятностей к единице, а затем смотришь в какой участок отрезка попало твое случайное число (от 0 до 1). Timaxa007 недавно выкладывал подобную реализацию в какой-то теме (не смог найти), правда она мне тоже не понравилась :)
 
Я тебя услышал по поводу массивов, но мне кажется, что это непринципиально. В любом случае, это просто изначальный код: ковыряясь в нем время от времени, я периодически принимаюсь его чистить и совершенствовать, и наверное, в конце концов когда-нибудь он станет таким, что понравится даже тебе.


bp6GJfTGUG.png

Название отражает её принцип действия. Линейность есть? Есть. Рандом есть? Есть. Он убывает? Убывает. Что я не так написал?
Что я хочу, я сказал в топике темы. Проиллюстрировал, на всякий случай:
Верхний график - это работа MathHelper.getRandomIntegerInRange();
Нижний график - это работа моей функции. Причем параметрами вызова функции можно двигать отрезок влево и вправо и управлять крутизной его наклона.
Я сразу сказал, что я не претендую на революционный прорыв в какой-либо из сфер программирования. Я просто поделился своим кодом. Если ты переделаешь и выложишь, а лучше - напишешь в эту тему другую функцию с такой же (или лучшей) функциональностью, да ещё и менее ресурсоемкую, да еще и с подробным описанием её работы - я буду только рад. Если не напишешь - кто-то придёт и позаимствует мою функцию. Важно лишь то, что в этой теме будет лежать одно или несколько готовых решений.
 
Я очень сомневаюсь, что твоя функция пригодна для твоей цели: "чтобы, например, крутой алмазный шмот выпадал реже, чем какой-нибудь кожаный)"
Тебе нужно такое:
277eacb40c514d7b91c23e6a894d5d14.png


48835a3c990f429289bf843c320fcb25.png

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

По моему Dahaka правильно все советует - просто задать все вручную таблицей какой-нибудь.
 
Dahaka написал(а):
Я очень сомневаюсь, что твоя функция пригодна для твоей цели: "чтобы, например, крутой алмазный шмот выпадал реже, чем какой-нибудь кожаный)"
Тебе нужно такое:
277eacb40c514d7b91c23e6a894d5d14.png


48835a3c990f429289bf843c320fcb25.png

При этом совсем не важен порядок вхождения.

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

Если взять, как у тебя, четыре предмета, то:


Пишем вес: 0.1 - 10.0.
Разница: 9.9, делю на 3, получаю шаг 3.3.
Вес первого 10.0, второго - 6.7, третьего - 3.4, четвертого - 0.1.
Складываю все, получаю 20.2.
Беру от этого числа рандомное. Понятно, что четвертый итем выпадет, если выпадет число от 20.1 до 20.2, а первый итем - если выпадет от 0 до 4.0.

Объясни мне, где я ошибаюсь?




Asd73 написал(а):
Если так, то первый рисунок верен, но во втором ошибка - это будет не прямая линия.

Как человек с плохим знанием математики, я допускаю, что второй график не совсем верно отображает то, что я имел в виду. Но сути это не меняет. Почему - я объяснил только что.

Еще раз повторю: если вы считаете, что можете сделать то же самое лучше, просто напишите сюда готовое решение на языке Java, к чему все эти рассуждения о высоких материях?

Update:

Для наглядности - вам, и проверки правильности работы функции - мне, я протестировал функцию таким кодом:

Код:
private void test() {
 long startTime = System.currentTimeMillis();
Random rand = new Random(startTime);
int minNumber = 1, maxNumber = 20;
int[] goalcounter = new int[1 + maxNumber - minNumber ];
double minWeight = 0.1D, maxWeight = 10.0D;

for(int i = 0; i < 1000000; i++){
goalcounter [Functions.decreasingRandom(minNumber, maxNumber, minWeight, maxWeight, rand) - minNumber]++;
}

for(int j = 0; j < maxNumber - minNumber; j++){
System.out.println((j + minNumber) + ": " + goalcounter[j]);
}
System.out.println("Done in " + (System.currentTimeMillis() - startTime) + " ms.");


FMLCommonHandler.instance().exitJava(0, true);
}

Вот результат:

RNLHLHL4Bp.png


Для тех, кому неохота вникать в цифры/код:
Делаю 1000 попыток, вес -  0.1...10.0. Первое число (это кожаная броня) выпало 104 раза, а последнее (алмазная) - 2 раза. По-моему, все работает так, как я планировал.

То же самое для миллиона попыток:

2yDFdUrXO8.png


И вот еще: (диапазон 50...100, миллион попыток):

FDASg6yLcS.png
 
Ммм, работает. Найс.
Но все же большой минус, что линейно (хоть это и соответствует названию темы). Реализация через таблицу намного юзабельнее. Попозже выложу может быть, т.к. мне и самому понадобится.
 
Я соглашусь с Dahaka и Asd73 что можно сделать более удобно и практично, но LaoTheLizard постарался и выложил результат в учебник, к тому же всё подробно описал. Так что за эту работу ему респект, учтём еще то что в учебнике уже давно не было примеров/уроков. У каждого своя реализация, думаю будет хорошо если Dahaka выложит свой вариант.
 
Сюда выложить? Или может отдельную тему? :) Мой рандом никак не линейный.
 
Dahaka написал(а):
Сюда выложить? Или может отдельную тему? :) Мой рандом никак не линейный.

Лучше сделай новую, ибо в постах темы искать не удобно. Да и плюс как ты уже сказал он у тебя не линейный.
 
Один я не понимаю, что тут происходит?
 
Не, 7 человек сказали, что полезная функция. Скорее всего понимают.
 
Назад
Сверху