根据特定条件生成部分随机布尔值的数组

时间:2016-01-31 16:03:06

标签: arrays random language-agnostic

我正在尝试根据某些标准生成随机数据。我被困在如何开始所以我没有任何代码。

我只想要一些关于如何实现这一目标的指南。您无需提供完整的代码。

因此,遇到问题,我们可以说我们有这些现有数据:

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中的解决方案,但欢迎使用任何语言。

3 个答案:

答案 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();
    }
}

enter image description here

第二种变体 - 蛮力。实际上,这项任务可能比遗传算法更快。您也可以使用它获得所有可能的变体。

    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;
    }
}

enter image description here

答案 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)

我发布了一个答案,然后注意到我错过了一些关键要求。我添加并更改了一些内容以解决这些缺失的元素。

核心方法在大多数情况下都会失败,但它足够快,你可以在循环中完成它,直到你得到一个好的答案。根据实际价值,在法律结果很少的情况下,您似乎需要运气好。

使用的步骤:

  • 选择最长条纹的随机点(示例中为Win)
  • 在设置残羹剩饭时对损坏进行处理以防止延长
  • 查找具有足够连续位置的索引以保持亏损连胜
  • 选择一个随机的并设置损失条纹(如果没有则返回)
  • 将所有剩余物品设为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个中找到一个。