根据某些标准生成部分随机布尔值数组[关闭]
Posted
技术标签:
【中文标题】根据某些标准生成部分随机布尔值数组[关闭]【英文标题】:Generating an array of partially random boolean values based on certain criteria [closed] 【发布时间】:2016-05-09 00:58:43 【问题描述】:我正在尝试根据某些标准生成随机数据。我被困在如何开始,所以我没有任何代码。
我只是想要一些关于如何实现这一目标的指南。您无需提供完整的代码。
所以,说到问题,假设我们有这些现有数据:
Total - 10
Won - 7
Lost - 3
Longest Winning Streak - 5
Longest Losing Streak - 2
现在我需要生成满足上述条件的随机布尔值数组(true
代表胜利,false
代表失败)。
因此,在这种情况下,输出可以是以下任何一种:
0011011111
1111101100
1010011111
..........
困扰我的是连胜部分。如果不是连胜,我可以生成七个1(s)
和三个0(s)
,然后随机洗牌。
注意:我更喜欢 C#、VB.NET、javascript 或 Python 的解决方案,但欢迎使用任何语言。
【问题讨论】:
1.你已经尝试过什么? 2. 欢迎任何语言?坏主意,因为我们只解决special issues,如果您的问题接受所有语言,那么就没有正确答案。 一种方法是创建所有输赢组合,然后过滤掉不符合连胜标准的组合。 我应该把这个贴在 Code Golf 上吗? @KevinGuan 如果您愿意阅读this,您会发现这个问题并非离题。并且还允许询问常见的编程问题/算法 afaik @KevinGuan 我当然可以只制作这个 C#,但由于它与基本数据类型相关,而且 .NET 框架没有发挥作用,我认为最好不要制作这个特定语言,因为它将提供更广泛的回答范围。 【参考方案1】:我建议使用遗传算法。
class Program
static int len = 10;
static int win = 7;
static int lws = 5;
static int lls = 2;
static Random rnd = new Random();
static void Main(string[] args)
int genSz = 15;
var generation = new List<Chromosome>();
Helper.Repeat(() => generation.Add(new Chromosome()), genSz);
int gen = 1;
while (generation.First().Fitness != 0)
//procreation
Helper.Repeat(() =>
int x1 = rnd.Next(genSz / 2);
int x2 = rnd.Next(genSz);
generation.Add(new Chromosome(generation[x1], generation[x2]));
, genSz);
//selection
generation = generation.OrderBy(x => x.Fitness).Take(genSz).ToList();
Console.WriteLine("GENERATION " + gen++);
foreach (var x in generation)
Console.WriteLine(x);
Console.ReadLine();
Console.ReadLine();
class Chromosome
bool[] genes = new bool[len];
public Chromosome()
public Chromosome(Chromosome p1, Chromosome p2)
//crossingover
rnd.Shuffle(ref p1, ref p2); //may reorder parents or not
var x = rnd.Next(len);
Array.Copy(p1.genes, 0, genes, 0, x);
Array.Copy(p2.genes, x, genes, x, len - x);
//mutation
if (rnd.Flip())
x = rnd.Next(len);
genes[x] = !genes[x];
public int Fitness
get
int w = genes.Count(g => g);
int l = len - w;
int ws = genes.LongestStreak(g => g);
int ls = genes.LongestStreak(g => !g);
return Math.Abs(w - win) + Math.Abs(lws - ws) + Math.Abs(lls - ls);
public override string ToString()
return "[" + new string(genes.Select(g => g ? '*' : '.').ToArray()) + "] " + Fitness.ToString();
public static class Helper
public static bool Flip(this Random rnd) => rnd.Next(2) == 0;
public static void Shuffle<T>(this Random rnd, ref T a, ref T b, bool allowNoChange = true)
if (allowNoChange && rnd.Flip()) return; //no reordering
T tmp = a; a = b; b = tmp;
public static int LongestStreak<T>(this IEnumerable<T> sequence, Predicate<T> selector)
int current = 0;
int longest = 0;
foreach (T x in sequence)
if (selector(x))
current++;
if (current > longest) longest = current;
else
current = 0;
return longest;
public static void Repeat(this Action action, int N)
for (int n = 0; n < N; n++) action();
第二种变体 - 蛮力。如果序列很短,可以使用。您还可以使用它获得所有可能的变体。
class Program
static void Main(string[] args)
var res = new[] true, false .Words(10).Where(w =>
return
w.Count(g => g) == 7 &&
w.LongestStreak(g => g) == 5 &&
w.LongestStreak(g => !g) == 2;
);
foreach (var v in res)
foreach (var b in v)
Console.Write(b ? "*" : ".");
Console.WriteLine();
Console.WriteLine(res.Count());
Console.ReadLine();
public static class Helper
public static IEnumerable<IEnumerable<T>> Words<T>(this IEnumerable<T> alphabet, int len)
foreach (var l in alphabet)
if (len == 1)
yield return l.Yield();
else
foreach (var w in alphabet.Words(len - 1))
yield return w.Prepend(l);
public static IEnumerable<T> Yield<T>(this T item)
yield return item;
static IEnumerable<T> Prepend<T>(this IEnumerable<T> rest, T first)
yield return first;
foreach (var item in rest)
yield return item;
public static int LongestStreak<T>(this IEnumerable<T> sequence, Predicate<T> selector)
int current = 0;
int longest = 0;
foreach (T x in sequence)
if (selector(x))
current++;
if (current > longest) longest = current;
else
current = 0;
return longest;
【讨论】:
这很好,但你能测试一下111,60,10,4
并给出输出截图吗?目前 python 甚至没有在我的系统上启动。
@Fᴀʀʜᴀɴ Aɴᴀᴍ,第二种变体不适合这种情况,但遗传算法很快找到了解决方案:i.gyazo.com/976fda59612a39509b0ddda408e1e4c6.png
太棒了。接受这个答案。
好的,现在我在我的电脑上,但这不起作用。哦不,似乎每一代之后我都没有按下键。等我最后一次测试一下。
您可能还选择了一个错误的版本,其中将失败次数视为错误。确保没有 lst 变量。【参考方案2】:
我的建议是使用算法从长度-n
(您的 total
数字)字符串中选择 k
位(您的 won
数字)。在这里,我使用@foglebird 写的kbits(n, k)
function。然后,您可以使用列表推导过滤掉不需要的排列。
import itertools
def kbits(n, k):
result = []
for bits in itertools.combinations(range(n), k):
s = ['0'] * n
for bit in bits:
s[bit] = '1'
result.append(''.join(s))
return result
total = 10
won = 7
lost = 3
max_win = 5
max_lose = 2
answer = [x for x in kbits(total, won) if '1'*(max_win+1) not in x and '0'*(max_lose+1) not in x]
【讨论】:
测试它here,我发现它输出了一个布尔数组数组。如何过滤掉正确的?【参考方案3】:我发布了一个答案,然后发现我错过了一些关键要求。我添加并更改了一些内容以解决这些缺失的元素。
核心方法在大多数情况下都会失败,但它的速度非常快,以至于您可以循环执行,直到得到一个好的答案。根据实际值,在法律结果很少的情况下,您似乎需要运气。
使用的步骤:
为最长的连胜选择一个随机点(在示例中为获胜) 用损失括起来以防止在设置剩余时延长它 找到具有足够连续槽的索引以保持连续丢失 随机选择一个并设置连续亏损(如果没有则返回) 将所有 Leftovers 设置为Not the value at n-1
以避免扩展或创建新的连胜
因此,无论 WinCount 和 LossCount 是否正确,都是命中或未命中。这似乎比大小合适的条纹更容易被发现。包装器方法测试结果以拒绝并重新运行。使用给定的值,它通常会在前 10 次左右找到获胜者。
构造字符串表示的核心方法,以及一个helper:
' ToDo change to return Bool() = string is easier to read
Private Function FarhamStreaks(winStrk As Int32, loseStrk As Int32, total As Int32) As String
' -1 == not set
Dim result = Enumerable.Repeat(-1, total).ToArray
' set longest streak first
Dim wNDX = RNG.Next(0, total + 1 - winStrk)
For n As Int32 = 0 To winStrk - 1
result(wNDX + n) = 1
Next
' bracket with losers so the w streak cant extend
If wNDX > 0 Then result(wNDX - 1) = 0
If wNDX + winStrk < result.Length - 1 Then result(wNDX + winStrk) = 0
' look for eligible consecutive starting slots
' might be none
Dim lossNdx As New List(Of Int32)
For n As Int32 = 0 To result.Count - 1
Dim count = CountConsecutiveLooserSlotsFrom(n, result)
If (n + 1) < result.Count AndAlso count >= loseStrk Then
lossNdx.Add(n)
End If
Next
If lossNdx.Count = 0 Then
' do over
' the code has never gotten here
' but depends on the mix of values
Return ""
End If
' set losses
Dim lNdx = lossNdx(RNG.Next(0, lossNdx.Count))
For n As Int32 = 0 To loseStrk - 1
result(lNdx + n) = 0
Next
' set the leftovers based on next value to avoid
' extending streaks
For n As Int32 = 0 To result.Length - 1
If result(n) = -1 Then
If n > 0 Then
result(n) = If(result(n - 1) = 0, 1, 0)
Else
result(n) = If(result(n + 1) = 0, 1, 0)
End If
End If
Next
Dim resultString = String.Join(",", result)
' convert to boolean
Dim realResult(total) As Boolean
For n As Int32 = 0 To total - 1
realResult(n) = Convert.ToBoolean(result(n))
Next
Return resultString
End Function
' find candidate slots for the shorter streak:
Private Function CountConsecutiveLooserSlotsFrom(ndx As Integer, theArray As Int32()) As Int32
Dim count As Int32 = 1 ' including ndx
For n As Int32 = ndx To theArray.Length - 2
If theArray(n) <> 1 AndAlso theArray(n + 1) <> 1 Then
count += 1
Else
Exit For
End If
Next
Return count
End Function
验证候选结果(和性能指标)的方法:
Private Function MakeFarhamStreak(wins As Int32, winStreak As Int32,
lossStreak As Int32,
total As Int32) As String
Const MaxTries As Int32 = 999
Dim losses = (total - wins)
Dim reverse As Boolean = (lossStreak > winStreak)
Dim candidate As String
Dim sw As New Stopwatch
Dim pass, fail As Int32
Dim count As Int32
sw.Start()
For n As Int32 = 0 To MaxTries
If reverse Then
candidate = FarhamStreaks(lossStreak, winStreak, total)
' to do: un-reverse (Not) the results -
Else
candidate = FarhamStreaks(winStreak, lossStreak, total)
End If
Dim result = candidate.Split(","c)
' test win count
count = candidate.Where(Function(f) f = "1").Count
If count <> wins Then
fail += 1
Continue For
End If
' test loss count
count = candidate.Where(Function(f) f = "0").Count
If count <> losses Then
fail += 1
Continue For
End If
Dim tmp = candidate.Replace(","c, "")
' test win streak size
Dim wstreaks = tmp.Select(Function(c, i) tmp.Substring(i).
TakeWhile(Function(q) q = c AndAlso q = "1").
Count()).
Max
If wstreaks <> winStreak Then
fail += 1
Continue For
End If
Dim lstreaks = tmp.Select(Function(c, i) tmp.Substring(i).
TakeWhile(Function(q) q = c AndAlso q = "0").
Count()).
Max
If lstreaks <> lossStreak Then
fail += 1
Continue For
End If
pass += 1
If pass = 1 Then
Console.WriteLine("First Pass in 0ms (try # 1 = 2)",
sw.ElapsedMilliseconds, n, candidate)
' normally, return at this point
End If
Next
End Function
将较短的条纹放在较长的条纹周围更容易,因此它会根据需要颠倒 parm 顺序。没有代码可以翻转/没有结果。
结果:
在 18 毫秒内首次通过(尝试 #4 = 1,1,1,1,1,0,0,1,0,1) 失败总数 753 75.38% 总通过率 247 24.72% 999 个候选人的总时间 29ms
它在尝试 #4 中找到了第一个传递值 - 对于 10、7w、5ws、2ls 值,它通常在前 10 个中找到一个。
【讨论】:
以上是关于根据某些标准生成部分随机布尔值数组[关闭]的主要内容,如果未能解决你的问题,请参考以下文章