任何人都可以为轮盘赌选择功能提供一些伪代码吗?我将如何实现这个:我真的不明白如何阅读这个数学符号。我想要通用算法。
12 回答
其他答案似乎是假设您正在尝试实施轮盘赌游戏。我认为您是在询问进化算法中的轮盘赌选择。
下面是一些实现轮盘选择的 Java 代码。
假设您有 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。
这是一些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
首先,生成您分配的百分比数组,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
有两个步骤:首先创建一个包含轮子上所有值的数组。这可以是一个带有颜色和数字的二维数组,或者您可以选择将 100 添加到红色数字。
然后简单地生成一个介于 0 或 1 之间的随机数(取决于您的语言是从 0 还是 1 开始对数组索引进行编号)和数组中的最后一个元素。
大多数语言都有内置的随机数函数。在 VB 中,VBScript
函数是RND()
. 在 Javascript 中是Math.random()
从数组中的该位置获取值,您就有了随机轮盘赌号码。
最后一点:不要忘记播种随机数生成器,否则每次运行程序时都会得到相同的抽奖序列。
这是使用 Java 中的流选择的一种非常快速的方法。它使用值作为权重来选择数组的索引。由于数学特性,不需要累积权重。
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 求和或将双打作为可迭代读取来进一步改进。
我也想要同样的东西,所以创建了这个独立的轮盘赌类。你给它一系列权重(以双数组的形式),它会根据加权随机选择从该数组中简单地返回一个索引。
我创建了一个类,因为你可以通过构造函数只进行一次累积加法来获得很大的速度。它是 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 权重等等。
您可以使用这样的数据结构:
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;
}
好吧,对于美式轮盘,您需要生成一个介于 1 到 38 之间的随机整数。有 36 个数字,一个 0 和一个 00。
不过,要考虑的一件大事是,在美式轮盘赌中,可以进行许多不同的赌注。一次投注可以覆盖 1、2、3、4、5、6、两个不同的 12 或 18。您可能希望创建一个列表列表,其中每个数字都有额外的标志以简化它,或者在编程中完成所有操作.
如果我在 Python 中实现它,我只需创建一个 0、00 和 1 到 36 的元组,并为每次旋转使用 random.choice()。
这假设某个类“分类器”只有一个字符串条件、字符串消息和双重强度。只要按照逻辑。
——保罗
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;
}
我编写了一个类似于 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
该代码还以零概率测试轮盘赌。
恐怕任何在所有编程语言中使用内置随机数生成器的人都必须意识到生成的数字不是 100% 随机的。因此应谨慎使用。
随机数生成器伪代码
- 向顺序计数器加一
- 获取顺序计数器的当前值
- 通过计算机滴答计数或其他一些小间隔计时器值添加计数器值
- 可选地添加加法数字,例如来自外部硬件(如等离子发生器)的数字或一些其他类型的有些随机现象
- 例如,将结果除以一个非常大的素数 359334085968622831041960188598043661065388726959079837
- 从结果的小数点最右边获取一些数字
- 使用这些数字作为随机数
使用随机数字为轮盘赌创建 1 到 38(或 37 欧洲)之间的随机数字。