【问题标题】:Roulette wheel selection algorithm [duplicate]轮盘选择算法[重复]
【发布时间】:2010-09-22 20:02:23
【问题描述】:

谁能提供一些轮盘选择功能的伪代码?我将如何实现这个:我真的不明白如何阅读这个数学符号。我想要通用算法。

【问题讨论】:

  • 已编辑标签。有人从这个问题的第一个修订版中删除了“遗传”标签,使其不太清楚所问的内容。
  • 无符号整数 = ::rand() % 37;

标签: algorithm genetic-algorithm evolutionary-algorithm roulette-wheel-selection


【解决方案1】:

有两个步骤:首先创建一个包含轮子上所有值的数组。这可以是一个带有颜色和数字的二维数组,或者您可以选择在红色数字上加 100。

然后简单地生成一个介于 0 或 1(取决于您的语言是从 0 还是 1 开始对数组索引进行编号)和数组中的最后一个元素之间的随机数。

大多数语言都有内置的随机数函数。在 VB 和VBScript 中,函数是RND()。在 Javascript 中是 Math.random()

从数组中的那个位置获取值,你就有了你的随机轮盘赌号码。

最后说明:不要忘记为您的随机数生成器设置种子,否则每次运行程序时您都会得到相同的抽奖顺序。

【讨论】:

    【解决方案2】:

    随机数生成器伪代码

    • 向顺序计数器加一
    • 获取顺序计数器的当前值
    • 通过计算机滴答计数添加计数器值或其他一些小的间隔计时器值
    • 可以选择添加加法数字,例如来自外部硬件(如等离子发生器)的数字或一些其他类型的有些随机现象
    • 将结果除以一个非常大的素数 例如 359334085968622831041960188598043661065388726959079837
    • 从结果的小数点最右边获取一些数字
    • 将这些数字用作随机数

    使用随机数字为轮盘赌创建介于 1 和 38(或 37 欧洲)之间的随机数字。

    【讨论】:

    • 我认为随机数已经在每一种编程语言中都得到了解决,这个过程不再需要手工完成。你为什么不提到在你做的时候把所有的半加器连接起来?
    【解决方案3】:

    好吧,对于美式轮盘,您需要生成一个介于 1 到 38 之间的随机整数。有 36 个数字,一个 0 和一个 00。

    不过,需要考虑的一件大事是,在美式轮盘赌中,可以进行许多不同的赌注。一次投注可以覆盖 1、2、3、4、5、6、两个不同的 12 或 18。您可能希望创建一个列表列表,其中每个数字都有额外的标志以简化它,或者在编程中完成所有操作.

    如果我在 Python 中实现它,我只需创建一个 0、00 和 1 到 36 的元组,并为每次旋转使用 random.choice()。

    【讨论】:

      【解决方案4】:

      其他答案似乎是假设您正在尝试实施轮盘赌游戏。我想你问的是进化算法中的轮盘赌选择。

      Here is some Java code 实现轮盘选择。

      假设您有 10 个项目可供选择,并且您通过生成一个介于 0 和 1 之间的随机数进行选择。您将 0 到 1 的范围划分为十个不重叠的段,每个段与十个项目之一的适应度成正比.例如,这可能如下所示:

      0 - 0.3 is item 1
      0.3 - 0.4 is item 2
      0.4 - 0.5 is item 3
      0.5 - 0.57 is item 4
      0.57 - 0.63 is item 5
      0.63 - 0.68 is item 6
      0.68 - 0.8 is item 7
      0.8 - 0.85 is item 8
      0.85 - 0.98 is item 9
      0.98 - 1 is item 10
      

      这是您的轮盘赌。你在 0 和 1 之间的随机数是你的旋转。如果随机数是0.46,那么选择的项目就是项目3。如果是0.92,那么就是项目9。

      【讨论】:

      • 这是我遇到过的最快的。非常好。
      • 没有人谈论替换所选项目以使所选项目不再被选中。有什么解决办法吗?
      • @AnikIslamAbhi 就我而言,轮盘赌选择假设每个项目都可以选择多次。如果您将N 时间随机化(其中N 是人口计数),您将在选择后采取完全相同的人口。
      【解决方案5】:

      首先,生成一个由您分配的百分比组成的数组,比如说p[1..n] 并假设总数是所有百分比的总和。

      然后得到一个介于 1 到总数之间的随机数,比如说r

      现在,lua 中的算法:

      local  c  =  0
      for i = 1,n do
          c = c + p[i]
          if r <= c then
              return i
          end
      end
      

      【讨论】:

        【解决方案6】:

        这假设某个类“分类器”只有一个字符串条件、字符串消息和双重强度。顺其自然就好。

        -- 保罗

        public static List<Classifier> rouletteSelection(int classifiers) {
            List<Classifier> classifierList = new LinkedList<Classifier>();
            double strengthSum = 0.0;
            double probabilitySum = 0.0;
        
            // add up the strengths of the map
            Set<String> keySet = ClassifierMap.CLASSIFIER_MAP.keySet();
            for (String key : keySet) {
                /* used for debug to make sure wheel is working.
                if (strengthSum == 0.0) {
                ClassifierMap.CLASSIFIER_MAP.get(key).setStrength(8000.0);
                }
                 */
                Classifier classifier = ClassifierMap.CLASSIFIER_MAP.get(key);
                double strength = classifier.getStrength();
                strengthSum = strengthSum + strength;
            }
            System.out.println("strengthSum: " + strengthSum);
        
            // compute the total probability. this will be 1.00 or close to it.
            for (String key : keySet) {
                Classifier classifier = ClassifierMap.CLASSIFIER_MAP.get(key);
                double probability = (classifier.getStrength() / strengthSum);
                probabilitySum = probabilitySum + probability;
            }
            System.out.println("probabilitySum: " + probabilitySum);
        
            while (classifierList.size() < classifiers) {
                boolean winnerFound = false;
                double rouletteRandom = random.nextDouble();
                double rouletteSum = 0.0;
        
                for (String key : keySet) {
                    Classifier classifier = ClassifierMap.CLASSIFIER_MAP.get(key);
                    double probability = (classifier.getStrength() / strengthSum);
                    rouletteSum = rouletteSum + probability;
                    if (rouletteSum > rouletteRandom && (winnerFound == false)) {
                        System.out.println("Winner found: " + probability);
                        classifierList.add(classifier);
                        winnerFound = true;
                    }
                }
            }
            return classifierList;
        }
        

        【讨论】:

          【解决方案7】:

          这是一段python代码:

          def roulette_select(population, fitnesses, num):
              """ Roulette selection, implemented according to:
                  <http://stackoverflow.com/questions/177271/roulette
                  -selection-in-genetic-algorithms/177278#177278>
              """
              total_fitness = float(sum(fitnesses))
              rel_fitness = [f/total_fitness for f in fitnesses]
              # Generate probability intervals for each individual
              probs = [sum(rel_fitness[:i+1]) for i in range(len(rel_fitness))]
              # Draw new population
              new_population = []
              for n in xrange(num):
                  r = rand()
                  for (i, individual) in enumerate(population):
                      if r <= probs[i]:
                          new_population.append(individual)
                          break
              return new_population
          

          【讨论】:

          • 即使某些适应度值是负数,这也会起作用吗??
          • 不,不会。但是你不禁要问,既然适应度对应的是抽取那个样本的概率,那么就不存在抽取样本的负概率,那么你会期待什么样的行为呢?
          • 我的意思是,我有一个给出负值的适应度函数。假设我正在解决一个适合度为“错误”的问题,即。目标值与 GA 获得的值的差异。在这种情况下,适应度函数将产生负值。我如何将其公式化为一个 Router 轮?
          • 就像我说的,你必须考虑你想用这些负值做什么!轮盘赌不将 fitness 值作为输入,而是(未标准化)probabilities。您必须想出一种方法来将这些可能为负的错误值转化为概率。如果你可以有负的 error 值,你正在做一些奇怪的事情,但也许你可以否定它们(error = -error),那么我通常做的是fitness = 1/(1+error)
          • 轮盘选择不适用于负适应度值。尝试锦标赛选择。
          【解决方案8】:

          你可以使用这样的数据结构:

          Map<A, B> roulette_wheel_schema = new LinkedHashMap<A, B>()
          

          其中 A 是一个整数,表示轮盘赌的一个口袋,B 是标识种群中染色体的索引。口袋的数量与每条染色体的适应度成正比:

          口袋数 =(适合度比例)·(比例因子)

          然后我们生成一个介于 0 和选择模式大小之间的随机数,并使用这个随机数从轮盘赌中获得染色体的索引。

          我们计算每条染色体的适应度比例与被选择方案选择的概率之间的相对误差。

          getRouletteWheel方法返回基于先前数据结构的选择方案。

          private Map<Integer, Integer> getRouletteWheel(
                  ArrayList<Chromosome_fitnessProportionate> chromosomes,
                  int precision) {
          
              /*
               * The number of pockets on the wheel
               * 
               * number of pockets in roulette_wheel_schema = probability ·
               * (10^precision)
               */
              Map<Integer, Integer> roulette_wheel_schema = new LinkedHashMap<Integer, Integer>();
              double fitness_proportionate = 0.0D;
              double pockets = 0.0D;
              int key_counter = -1;
              double scale_factor = Math
                      .pow(new Double(10.0D), new Double(precision));
              for (int index_cromosome = 0; index_cromosome < chromosomes.size(); index_cromosome++){
          
                  Chromosome_fitnessProportionate chromosome = chromosomes
                          .get(index_cromosome);
                  fitness_proportionate = chromosome.getFitness_proportionate();
                  fitness_proportionate *= scale_factor;
                  pockets = Math.rint(fitness_proportionate);
                  System.out.println("... " + index_cromosome + " : " + pockets);
          
                  for (int j = 0; j < pockets; j++) {
                      roulette_wheel_schema.put(Integer.valueOf(++key_counter),
                              Integer.valueOf(index_cromosome));
                  }
              }
          
              return roulette_wheel_schema;
          }
          

          【讨论】:

            【解决方案9】:

            我也想要这样,所以创建了这个独立的轮盘赌课程。你给它一系列权重(以双精度数组的形式),它会根据加权随机选择从该数组中简单地返回一个索引。

            我创建了一个类,因为您只需通过构造函数进行一次累积加法即可获得很大的速度。这是 C# 代码,但享受 C 一样的速度和简单性!

            class Roulette
            {
                double[] c;
                double total;
                Random random;
            
                public Roulette(double[] n) {
                    random = new Random();
                    total = 0;
                    c = new double[n.Length+1];
                    c[0] = 0;
                    // Create cumulative values for later:
                    for (int i = 0; i < n.Length; i++) {
                        c[i+1] = c[i] + n[i];
                        total += n[i];
                    }
                }
            
                public int spin() {
                    double r = random.NextDouble() * total;     // Create a random number between 0 and 1 and times by the total we calculated earlier.
                    //int j; for (j = 0; j < c.Length; j++) if (c[j] > r) break; return j-1; // Don't use this - it's slower than the binary search below.
            
                    //// Binary search for efficiency. Objective is to find index of the number just above r:
                    int a = 0;
                    int b = c.Length - 1;
                    while (b - a > 1) {
                        int mid = (a + b) / 2;
                        if (c[mid] > r) b = mid;
                        else a = mid;
                    }
                    return a;
                }
            }
            

            初始权重由您决定。也许它可能是每个成员的适应度,或者与成员在“前 50 名”中的位置成反比的值。例如:第 1 名 = 1.0 权重,第 2 名 = 0.5,第 3 名 = 0.333,第 4 名 = 0.25 权重等等。

            【讨论】:

              【解决方案10】:

              这是使用 Java 中的流选择的一种非常快速的方法。它使用值作为权重来选择数组的索引。由于mathematical properties,不需要累积权重。

              static int selectRandomWeighted(double[] wts, Random rnd) {
                  int selected = 0;
                  double total = wts[0];
              
                  for( int i = 1; i < wts.length; i++ ) {
                      total += wts[i];            
                      if( rnd.nextDouble() <= (wts[i] / total)) selected = i;
                  }
              
                  return selected;        
              }
              

              如果数组太大而无法立即初始化,则可以使用 Kahan summation 或将双精度作为可迭代读取来进一步改进。

              【讨论】:

              • 这看起来是一个有趣的解决方案。尽管这个问题已经有好几年了(也许你看到了我的帖子),但我也和你在同一个小时内给出了答案。无论如何,我喜欢你的,因为它很短,但我认为我的效率可能更高,因为 O(log2 n) 效率而不是你的 O(n)。
              • 我认为您提出了问题,导致我发布了我的答案。无论如何,您的初始累积总和仍然需要O(n)。我认为这绝对是一个下限,无论如何:)
              • 只有第一次在构造函数中。在现实世界的案例中,您将进行很多“轮盘赌”(即寻找许多不同的父对)。这就是 O(log2 n) 发挥作用的地方,因为之后只调用 spin() 方法。
              • 如果您想从同一个分布中重复绘制,这是正确的。但根据我的经验,我认为这在实践中并没有发生太多。例如,在遗传算法中,适应度权重总是在变化。此外,如果您要从这样的分布中抽取大量样本,则根本不需要真正随机化,因为分数会收敛到归一化的权重。
              • 也许像你说的那样授予 GA。对于我需要它的情况(模拟数百万或数十亿的赌注来测试风险/利润),我的 O(log2 n) 答案代码是一个改进。我敢肯定还会有其他情况。
              【解决方案11】:

              恐怕任何在所有编程语言中使用内置随机数生成器的人都必须意识到生成的数字不是 100% 随机的。所以应该谨慎使用。

              【讨论】:

              • 感谢您提供宝贵的信息。
              【解决方案12】:

              我编写了一个类似于 Dan Dyer 的 Java 代码(前面提到过)。然而,我的轮盘赌根据概率向量(输入)选择单个元素并返回所选元素的索引。 话虽如此,如果选择大小是单一的并且您不假设如何计算概率并且允许零概率值,则以下代码更合适。该代码是独立的,包括一个带有 20 次轮子旋转的测试(运行)。

              import java.util.ArrayList;
              import java.util.Arrays;
              import java.util.List;
              import java.util.Random;
              import java.util.logging.Level;
              import java.util.logging.Logger;
              
              /**
               * Roulette-wheel Test version.
               * Features a probability vector input with possibly null probability values.
               * Appropriate for adaptive operator selection such as Probability Matching 
               * or Adaptive Pursuit, (Dynamic) Multi-armed Bandit.
               * @version October 2015.
               * @author Hakim Mitiche
               */
              public class RouletteWheel {
              
              /**
               * Selects an element probabilistically.  
               * @param wheelProbabilities elements probability vector.
               * @param rng random generator object
               * @return selected element index
               * @throws java.lang.Exception 
               */
              public int select(List<Double> wheelProbabilities, Random rng) 
                      throws Exception{
              
                  double[] cumulativeProba = new double[wheelProbabilities.size()];
                  cumulativeProba[0] = wheelProbabilities.get(0);
                  for (int i = 1; i < wheelProbabilities.size(); i++)
                  {
                      double proba = wheelProbabilities.get(i);
                      cumulativeProba[i] = cumulativeProba[i - 1] + proba;
                  }
                  int last = wheelProbabilities.size()-1;
                   if (cumulativeProba[last] != 1.0)
                   {
                          throw new Exception("The probabilities does not sum up to one ("
                                  + "sum="+cumulativeProba[last]);
                   }
                  double r = rng.nextDouble();
                  int selected = Arrays.binarySearch(cumulativeProba, r);
                   if (selected < 0)
                      {
                          /* Convert negative insertion point to array index.
                          to find the correct cumulative proba range index.
                          */
                          selected = Math.abs(selected + 1);
                      }
                   /* skip indexes of elements with Zero probability, 
                      go backward to matching index*/  
                  int i = selected; 
                  while (wheelProbabilities.get(i) == 0.0){
                      System.out.print(i+" selected, correction");
                      i--;
                      if (i<0) i=last;
                  }
                  selected = i;
                  return selected;
              }
              
              
              
                 public static void main(String[] args){
              
                 RouletteWheel rw = new RouletteWheel();
                 int rept = 20;
                 List<Double> P = new ArrayList<>(4);
                 P.add(0.2);
                 P.add(0.1);
                 P.add(0.6);
                 P.add(0.1);
                 Random rng = new Random();
                 for (int i = 0 ; i < rept; i++){
                     try {
                         int s = rw.select(P, rng);
                         System.out.println("Element selected "+s+ ", P(s)="+P.get(s));
                     } catch (Exception ex) {
                         Logger.getLogger(RouletteWheel.class.getName()).log(Level.SEVERE, null, ex);
                     }
                 }
                 P.clear();
                 P.add(0.2);
                 P.add(0.0);
                 P.add(0.5);
                 P.add(0.0);
                 P.add(0.1);
                 P.add(0.2);
                 //rng = new Random();
                 for (int i = 0 ; i < rept; i++){
                     try {
                         int s = rw.select(P, rng);
                         System.out.println("Element selected "+s+ ", P(s)="+P.get(s));
                     } catch (Exception ex) {
                         Logger.getLogger(RouletteWheel.class.getName()).log(Level.SEVERE, null, ex);
                     }
                 }
              }
              
               /**
               * {@inheritDoc}
               * @return 
               */
               @Override
               public String toString()
               {
                  return "Roulette Wheel Selection";
               }
              }
              

              下面是概率向量 P=[0.2,0.1,0.6,0.1] 的执行示例, WheelElements = [0,1,2,3]:

              元素选择 3,P(s)=0.1

              元素选择 2,P(s)=0.6

              元素选择 3,P(s)=0.1

              元素选择 2,P(s)=0.6

              元素选择 1,P(s)=0.1

              元素选择 2,P(s)=0.6

              元素选择 3,P(s)=0.1

              元素选择 2,P(s)=0.6

              元素选择 2,P(s)=0.6

              元素选择 2,P(s)=0.6

              元素选择 2,P(s)=0.6

              元素选择 2,P(s)=0.6

              元素选择 3,P(s)=0.1

              元素选择 2,P(s)=0.6

              元素选择 2,P(s)=0.6

              元素选择 2,P(s)=0.6

              元素选择 0,P(s)=0.2

              元素选择 2,P(s)=0.6

              元素选择 2,P(s)=0.6

              元素选择 2,P(s)=0.6

              代码还以零概率测试轮盘赌。

              【讨论】:

                猜你喜欢
                • 2020-08-06
                • 2014-05-25
                • 2010-09-15
                • 2018-04-13
                • 2012-09-28
                • 2011-05-02
                • 2018-07-04
                • 2019-07-06
                相关资源
                最近更新 更多