根据某些标准生成部分随机布尔值数组[关闭]

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 个中找到一个。

【讨论】:

以上是关于根据某些标准生成部分随机布尔值数组[关闭]的主要内容,如果未能解决你的问题,请参考以下文章

Java:生成一个恰好为“x”为真的随机布尔数组 - 算法

在性能方面,生成随机布尔值的最佳方法是啥?

生成随机布尔值的最快方法

JavaScript 生成随机布尔值

生成随机布尔概率

具有权重或偏差的随机布尔值