最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java實(shí)現(xiàn)游戲抽獎算法

 更新時間:2020年11月26日 12:44:17   作者:易成  
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)游戲抽獎算法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下

常用抽獎算法對比

基礎(chǔ)的游戲抽獎算法通常要求實(shí)現(xiàn)在指定獎品的集合中,每個獎品根據(jù)對對應(yīng)概率進(jìn)行抽取。個人了解的主要有以下幾中抽獎算法:

隨機(jī)數(shù)一一對應(yīng)

算法思想

這種算法思想最為簡單。將n個獎品編號0 - N-1,其中各類獎品的概率通過其數(shù)量體現(xiàn),最后程序產(chǎn)生0~n-1之間的隨機(jī)數(shù)便是抽中的獎品編號。例如:

蘋果手機(jī)概率1%,網(wǎng)站會員20%,折扣券20%,很遺憾59%。這樣,編號0是蘋果手機(jī),1-20是會員,21-40是折扣券,41~100是 很遺憾。產(chǎn)生的隨機(jī)數(shù)落在那個區(qū)間,就代表那個獎品被抽中。

存在問題

1、總數(shù)N快速膨脹

概率通過數(shù)量來體現(xiàn)在各個獎品概率較大的情況下,總數(shù)n可以較小。但如果在精度很高的情況下,總數(shù)必須按比例成倍擴(kuò)大。
例如,所有獎品概率都是10%,那么n只需要取10就可以。但是如果某個獎品概率是0.01%,按照這種算法,總數(shù)要擴(kuò)大到100*100。

2、平衡性影響

在Java中,Math.random()方法本身基本可以保證大量測試的情況下避免高重復(fù),且概率分布比較平均。但是需要注意的是,該方法默認(rèn)返回0-1之間的數(shù)據(jù)。

在當(dāng)前算法中,必須擴(kuò)大指定倍數(shù)并且強(qiáng)制使用int進(jìn)行類型轉(zhuǎn)換。在這樣的擴(kuò)大和轉(zhuǎn)換過程中,必然會對數(shù)據(jù)精度進(jìn)行修改,轉(zhuǎn)換后的數(shù)據(jù)也不能保證概率分布平均。

因此,該算法實(shí)際可能達(dá)不到預(yù)期的概率要求。

3、算法復(fù)雜度

數(shù)據(jù)準(zhǔn)備階段,為每個獎品確定編號與獎品信息的關(guān)系集合需要O(n);
產(chǎn)生隨機(jī)數(shù)階段并轉(zhuǎn)換,O(1);
從集合中查找,不同的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)不同,最差需要O(n);

離散法

算法思想

(高中數(shù)學(xué)里幾何概形的思想)
將獎品集合的概率劃分區(qū)段放入數(shù)組中。概率區(qū)段通過該概率累計(jì)相加確定。利用隨機(jī)數(shù)產(chǎn)生隨機(jī)概率,加入數(shù)組并排序,該數(shù)據(jù)的下標(biāo),就是對應(yīng)獎品集合中獎品的索引。例如,獎品的集合有X1,X2,X3,X4,對應(yīng)概率為P1=0.2,P2=0.2,P3=0.3,P4=0.3。
那么,產(chǎn)生的概率區(qū)段數(shù)組為[0.2,0.4,0.7,1.0]。
0.2以下代表X1,0.2-0.4代表X2,0.4-0.7代表X3,0.7~1代表X4。
這樣,如果產(chǎn)生一個隨機(jī)概率為0.5,加入數(shù)組排序后,0.4~0.7之間,是X3相加所在的概率區(qū)間,返回index=2。
由于區(qū)間分布的確定是按照X集合順序的,所以該索引也正是X3在原集合中的索引。

特點(diǎn)

1、利用幾何概形,概率數(shù)組分布在0到1之間,不再需要擴(kuò)大倍數(shù)和取整操作,基本可以保證概率平均分布,避免大量重復(fù)的情況
2、概率分配的排序過程,可以使用java默認(rèn)的排序工具類,也可以自己實(shí)現(xiàn)。保證時間復(fù)雜度最小。
3、復(fù)雜度
準(zhǔn)備階段,即對準(zhǔn)備概率集合,進(jìn)行歸一化等操作,假設(shè)樣本的概率個數(shù)為M個,則復(fù)雜度為O(m)。m遠(yuǎn)小于方法一中的n,因?yàn)楦怕手挥袔讉€,不會大量膨脹。
產(chǎn)生隨機(jī)數(shù),O(1)
排序取下標(biāo),根據(jù)排序算法,O(logM)即可實(shí)現(xiàn)
取值,根據(jù)下標(biāo),O(1);

Alias 算法

這種算法對數(shù)學(xué)要求比較高,沒有仔細(xì)研究。
感興趣的小伙伴可以自己研究一下和我分享

算法實(shí)現(xiàn)

獎品實(shí)體類

/**
 * 抽獎獎品實(shí)體類
 * @author irving
 * @since 2017年7月23日 下午9:41:33
 * @version MARK 0.0.1
 */
public class Gift {
 
 private int id; //獎品Id
 private String name; //獎品名稱
 private double prob; //獲獎概率
 
 public int getId() {
 return id;
 }
 public void setId(int id) {
 this.id = id;
 }
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name = name;
 }
 public double getProb() {
 return prob;
 }
 public void setProb(double prob) {
 this.prob = prob;
 }
 
 @Override
 public String toString() {
 return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE);
 }
}

抽獎實(shí)現(xiàn)工具類

/**
 * 抽獎工具類<br/>
 * 整體思想:
 * 獎品集合 + 概率比例集合
 * 將獎品按集合中順序概率計(jì)算成所占比例區(qū)間,放入比例集合。并產(chǎn)生一個隨機(jī)數(shù)加入其中,排序。</br>
 * 排序后,隨機(jī)數(shù)落在哪個區(qū)間,就表示那個區(qū)間的獎品被抽中。</br>
 * 返回的隨機(jī)數(shù)在集合中的索引,該索引就是獎品集合中的索引。</br>
 * 比例區(qū)間的計(jì)算通過概率相加獲得。
 * @author irving
 * @since 2017年7月23日 下午9:48:23
 * @version MARK 0.0.1
 */
package com.xxx.xxx.xxx.service;

import java.util.*;

/**
 * 抽獎工具類
 * @Author yuanyicheng
 * @Date 7:14 上午 2020/9/9
 */
public class DrawUtil {

 public static Gift draw(List<Gift> gifts) {
  if (null == gifts || gifts.size() == 0) {
   return null;
  }

  gifts.sort((o1, o2) -> (o1.prob - o2.prob) > 0 ? 1 : -1);

  List<Double> probLists = new ArrayList<>(gifts.size());
  Double sumProb = 0D;
  for (Gift gift : gifts) {
   sumProb += gift.getProb();
  }
  if (sumProb <= 0) {
   return null;
  }

  // 歸一化概率端點(diǎn)
  Double rate = 0D;
  for (Gift gift : gifts) {
   rate += gift.getProb();
   probLists.add(rate / sumProb);
  }

  double random = Math.random();
  probLists.add(random);
  Collections.sort(probLists);
  int index = probLists.indexOf(random);
  if (index >= 0) {
   return gifts.get(index);
  }
  return null;
 }

 public static void main(String[] args) {
  List<Gift> gifts = new ArrayList<>();
  Gift nothing = new Gift("謝謝惠顧", 0.5D);
  Gift vip = new Gift("XX會員1個月", 0.4D);
  Gift phone = new Gift("手機(jī)", 0.1D);

  gifts.add(nothing);
  gifts.add(phone);
  gifts.add(vip);

  // 抽獎
//  Gift g = draw(gifts);

  // 以下是測試統(tǒng)計(jì)
  Map<String, Integer> countMap = new HashMap<>();
  for (Gift gift: gifts) {
   countMap.put(gift.getName(), 0);
  }
  countMap.put("null", 0);
  for (int i=0; i<1000; i++) {
   // 抽一個
   Gift gift = draw(gifts);
   String name = "null";
   if (null != gift) {
    name = gift.getName();
   }

   int count = countMap.get(name);
   countMap.put(name, ++count);
  }

  for (Map.Entry<String, Integer> entry : countMap.entrySet()) {
   System.out.println("抽到"+entry.getKey()+", "+entry.getValue()+"次");

  }
 }
}

/**
 * 獎品類
 */
class Gift {
 String name;
 Double prob;

 public Gift(String name, Double prob) {
  this.name = name;
  this.prob = prob;
 }

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

 public Double getProb() {
  return prob;
 }

 public void setProb(Double prob) {
  this.prob = prob;
 }
}

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 淺談Spring解決jar包依賴的bom

    淺談Spring解決jar包依賴的bom

    這篇文章主要介紹了淺談Spring解決jar包依賴的bom,具有一定借鑒價值,需要的朋友可以參考下
    2017-12-12
  • java利用udp實(shí)現(xiàn)發(fā)送數(shù)據(jù)

    java利用udp實(shí)現(xiàn)發(fā)送數(shù)據(jù)

    這篇文章主要為大家詳細(xì)介紹了java利用udp實(shí)現(xiàn)發(fā)送數(shù)據(jù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-07-07
  • 如何將復(fù)雜SQL轉(zhuǎn)換成Java對象的實(shí)例講解

    如何將復(fù)雜SQL轉(zhuǎn)換成Java對象的實(shí)例講解

    轉(zhuǎn)換復(fù)雜SQL到Java代碼,我們需要確定數(shù)據(jù)庫連接方式和工具,使用JDBC的API來連接數(shù)據(jù)庫、執(zhí)行SQL語句,復(fù)雜SQL語句可以被拆分為多個步驟,每個步驟執(zhí)行一個特定的操作,通過將SQL語句拆分為多個步驟,我們可以更好地理解復(fù)雜SQL的邏輯,并且更容易將其轉(zhuǎn)換為Java代碼
    2024-05-05
  • 詳解如何在SpringBoot里使用SwaggerUI

    詳解如何在SpringBoot里使用SwaggerUI

    本篇文章主要介紹了詳解如何在SpringBoot里使用SwaggerUI,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-02-02
  • Java中的HashSet、LinkedHashSet集合解析

    Java中的HashSet、LinkedHashSet集合解析

    這篇文章主要介紹了Java中的HashSet、LinkedHashSet集合解析,與HashSet不同的是,LinkedHashSet在內(nèi)部使用了一個雙向鏈表來維護(hù)元素的順序,因此它可以保持元素的插入順序,這使得LinkedHashSet在需要保持元素順序的場景下非常有用,需要的朋友可以參考下
    2023-11-11
  • Java this super代碼實(shí)例及使用方法總結(jié)

    Java this super代碼實(shí)例及使用方法總結(jié)

    這篇文章主要介紹了Java this super代碼實(shí)例及使用方法總結(jié),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-03-03
  • spring boot 自定義參數(shù)過濾器,把傳入的空字符轉(zhuǎn)換成null方式

    spring boot 自定義參數(shù)過濾器,把傳入的空字符轉(zhuǎn)換成null方式

    這篇文章主要介紹了spring boot 自定義參數(shù)過濾器,把傳入的空字符轉(zhuǎn)換成null方式。具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • Java中的ThreadPoolExecutor線程池原理細(xì)節(jié)解析

    Java中的ThreadPoolExecutor線程池原理細(xì)節(jié)解析

    這篇文章主要介紹了Java中的ThreadPoolExecutor線程池原理細(xì)節(jié)解析,ThreadPoolExecutor是一個線程池,最多可使用7個參數(shù)來控制線程池的生成,使用線程池可以避免創(chuàng)建和銷毀線程的資源損耗,提高響應(yīng)速度,并且可以管理線程池中線程的數(shù)量和狀態(tài)等等,需要的朋友可以參考下
    2023-12-12
  • Java并發(fā)讀寫鎖ReentrantReadWriteLock 使用場景

    Java并發(fā)讀寫鎖ReentrantReadWriteLock 使用場景

    ReentrantReadWriteLock是Java中一種高效的讀寫鎖,適用于讀多寫少的并發(fā)場景,它通過允許多個線程同時讀取,但在寫入時限制為單線程訪問,從而提高了程序的并發(fā)性和性能,本文給大家介紹Java并發(fā)讀寫鎖ReentrantReadWriteLock 使用場景,感興趣的朋友跟隨小編一起看看吧
    2024-10-10
  • SpringBoot+mail 輕松實(shí)現(xiàn)各類郵件自動推送

    SpringBoot+mail 輕松實(shí)現(xiàn)各類郵件自動推送

    在實(shí)際的項(xiàng)目開發(fā)過程中,經(jīng)常需要用到郵件通知功能,例如,通過郵箱注冊,郵箱找回密碼,郵箱推送報表等等,實(shí)際的應(yīng)用場景非常的多,今天通過這篇文章,我們一起來學(xué)習(xí)如何在 Spring Boot 中快速實(shí)現(xiàn)一個自動發(fā)送郵件的功能
    2024-07-07

最新評論

马边| 泊头市| 乌什县| 乌鲁木齐市| 石家庄市| 河间市| 荥阳市| 开原市| 山东省| 安远县| 波密县| 赤峰市| 沛县| 灵宝市| 昭平县| 汉源县| 隆德县| 克什克腾旗| 沾益县| 织金县| 延长县| 高陵县| 达州市| 荥经县| 玉林市| 福鼎市| 婺源县| 高密市| 青铜峡市| 恩施市| 仙桃市| 永吉县| 体育| 富阳市| 横山县| 应城市| 宁阳县| 黎平县| 昌黎县| 桐梓县| 肃宁县|