我正在尝试根据某些标准生成随机数据。我被困在如何开始所以我没有任何代码。
我只想要一些关于如何实现这一目标的指南。您无需提供完整的代码。
因此,遇到问题,我们可以说我们有这些现有数据:
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中的解决方案,但欢迎使用任何语言。
答案 0 :(得分:3)
如果你不需要良好的表现,我建议采用遗传算法。
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;
}
}
答案 1 :(得分:1)
我的建议是使用算法从长度为k
(您的won
数字)字符串中选择n
位(您的total
号码)。在这里,我使用@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]
答案 2 :(得分:1)
我发布了一个答案,然后注意到我错过了一些关键要求。我添加并更改了一些内容以解决这些缺失的元素。
核心方法在大多数情况下都会失败,但它足够快,你可以在循环中完成它,直到你得到一个好的答案。根据实际价值,在法律结果很少的情况下,您似乎需要运气好。
使用的步骤:
Not the value at n-1
以避免延长或创建新条纹因此,无论WinCount和LossCount是否正确,它都会受到影响。这似乎比正确尺寸的条纹更容易发现。包装器方法测试结果以拒绝并重新运行。使用给定的值,它通常会在前10次左右找到胜利者。
构造字符串表示的核心方法和帮助器:
' 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 {0}ms (try # {1} = {2})",
sw.ElapsedMilliseconds, n, candidate)
' normally, return at this point
End If
Next
End Function
较短的条纹更容易适应更长的条纹,因此它会根据需要反转parm顺序。没有代码可以翻转/不是结果。
结果:
首次通过18ms(尝试#4 = 1,1,1,1,1,0,0,1,0,1)
总失败753 75.38%
总票数247 24.72%
999名候选人的总时间为29ms
它在try#4上找到了第一个传递值 - 使用10,7w,5ws,2ls值,它通常在前10个中找到一个。