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

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

  • Да.

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

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

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

    Голосов: 1 6.7%

  • Всего проголосовало
    15
210
1
19
Решил тут одевать зомби в случайный шмот из массива, сначала сделал выборку через 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. Можно использовать зерно мира.
Уверен, что я изобрел велосипед, что этот велосипед - инвалид, что есть более элегантные способы достичь желаемого результата, но мне эти способы неизвестны. Буду рад, если кому-то моя функция окажется полезной.
 
398
4
7
Почитай учебник по математике. Сумма большого количества случайных величин имеет закон распределения близкий к нормальному (гауссовскому).
Это доказывает центральная предельная теорема.
Не совсем понял для чего это все нужно, можно же просто какую-нибудь функцию, например экспоненту, от обычного рандома брать.
 
210
1
19
Да я в математике ни бум-бум, смысла нет читать учебник.
Для чего всё это нужно? Для того, чтобы одни предложили свои готовые рабочие варианты (более простые? более короткие? более эффективные? более быстрые?), а другие при надобности могли прийти и этими вариантами воспользоваться.
 
2,505
81
397
А можешь показать, как ты используешь свой метод дальше?
Слишком много у тебя каких-то вычислений. Сложный алгоритм.
 
210
1
19
Да, могу. Только осторожно, глаза не сломайте - я, как гинеколог, всё делаю через одно место. И местами бывает много комментариев, так как мод большой, боюсь, что если зайду в какой-то кусок кода через полгода - вообще не вспомню, что зачем писал.
Раз:
Код:
 /**
 * Одеваем свинозомби
 * @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;
 }
 
2,505
81
397
Что-то слишком сложное.
Кстати, не стоит каждый раз создавать массивы. Это слииишком непроизводительно. И не стоит использовать 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 недавно выкладывал подобную реализацию в какой-то теме (не смог найти), правда она мне тоже не понравилась :)
 
210
1
19
Я тебя услышал по поводу массивов, но мне кажется, что это непринципиально. В любом случае, это просто изначальный код: ковыряясь в нем время от времени, я периодически принимаюсь его чистить и совершенствовать, и наверное, в конце концов когда-нибудь он станет таким, что понравится даже тебе.


bp6GJfTGUG.png

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


48835a3c990f429289bf843c320fcb25.png

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

По моему Dahaka правильно все советует - просто задать все вручную таблицей какой-нибудь.
 
210
1
19
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
 
2,505
81
397
Ммм, работает. Найс.
Но все же большой минус, что линейно (хоть это и соответствует названию темы). Реализация через таблицу намного юзабельнее. Попозже выложу может быть, т.к. мне и самому понадобится.
 
667
7
2
Я соглашусь с Dahaka и Asd73 что можно сделать более удобно и практично, но LaoTheLizard постарался и выложил результат в учебник, к тому же всё подробно описал. Так что за эту работу ему респект, учтём еще то что в учебнике уже давно не было примеров/уроков. У каждого своя реализация, думаю будет хорошо если Dahaka выложит свой вариант.
 
2,505
81
397
Сюда выложить? Или может отдельную тему? :) Мой рандом никак не линейный.
 
667
7
2
Dahaka написал(а):
Сюда выложить? Или может отдельную тему? :) Мой рандом никак не линейный.

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