【问题标题】:Is there a more efficient way of creating versions of an array than brute force?有没有比蛮力创建数组版本更有效的方法?
【发布时间】:2016-05-02 08:45:32
【问题描述】:

我有一个bool[],我想从该原始数组生成该数组的版本/场景并将其保存到list<bool[]>

如果数组看起来像{true,true,false,true,true,true}。这个想法是,如果bool[0]bool[1] 都为真而bool[2] 为假。我想创建一个场景,其中 bool[0]bool[1] 为假,bool[2] 为真,并将该场景添加到我的列表中。我目前正在 for 循环中执行此操作,以检查数组中的每个插槽中的模式 {true,true,false}{false, true,true}。目标是找到我在数组中拥有尽可能少的bool = true 元素并返回该数组的场景。

我试图用蛮力来做这件事,但它太慢了(有时是几分钟)。我希望它适用的语言是 C#。有没有比蛮力更有效的方法来创建场景?

我用于生成场景的代码:

class Program
{
    public static List<bool[]> alternativs = new List<bool[]>();
    public static int arrayCount = 23;
    public static int tempCount = 0;
    static void Main(string[] args)
    {
        //this works fast
        bool[] easyInput = new bool[23] { true, true, false, false, false, false, true, true, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false };
        //this takes forever
        bool[] hardInput = new bool[23] { true, true, false, true, true, false, true, true, false, true, true, false, true, true, false, true, true, false, true, true, false, true, true };

        alternativs.Add(easyInput);
        while (alternativs.Count > 0)
        {
            for (int i = 0; i < 21; i++)
            {
                if (alternativs[0][i] == true && alternativs[0][i + 1] == true && alternativs[0][i + 2] == false)
                {
                    bool[] temp = new bool[23];
                    bool[] temp3 = new bool[23];

                    Array.Copy(alternativs[0], temp, 23);
                    Array.Copy(temp, temp3, 23);
                    Array.Reverse(temp3);
                    temp[i] = false;
                    temp[i + 1] = false;
                    temp[i + 2] = true;

                    if (!alternativs.Contains(temp) && !alternativs.Contains(temp3))
                    {
                        alternativs.Add(temp);
                    }
                }
                if (alternativs[0][i] == false && alternativs[0][i + 1] == true && alternativs[0][i + 2] == true)
                {
                    bool[] temp2 = new bool[23];
                    bool[] temp4 = new bool[23];

                    Array.Copy(alternativs[0], temp2, 23);
                    Array.Copy(temp2, temp4, 23);

                    temp2[i] = true;
                    temp2[i + 1] = false;
                    temp2[i + 2] = false;
                    if (!alternativs.Contains(temp2) && !alternativs.Contains(temp4))
                    {
                        alternativs.Add(temp2);
                    }
                }
            }
            tempCount = 0;
            for (int j = 0; j < 23; j++)
            {
                if (alternativs[0][j] == true)
                    tempCount++;
            }
            if (tempCount < arrayCount)
            {
                arrayCount = tempCount;
            }
            alternativs.RemoveAt(0);
        }
        Console.WriteLine(arrayCount);
    }
}

经过一些修改,代码如下所示:

class Program
{
    public static List<string> alternativs = new List<string>();
    public static int arrayCount = 23;
    public static int tempCount = 0;
    static void Main(string[] args)
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        //this works fast
        //string easyInput = "11000011110000000000000";
        //this takes forever
        string hardInput = "11011011011011011011011";
        string xtra = "00101100011010000000000";

        string TmpVal = "";
        string RevVal = "";

        alternativs.Add(xtra);
        while (alternativs.Count > 0)
        {
            if (alternativs[0].Contains("110"))
            {
                TmpVal = alternativs[0];
                TmpVal = TmpVal.Replace("110", "001");
                RevVal = string.Concat(Enumerable.Reverse(TmpVal)); //String Reverse

                if (!alternativs.Any(xs => xs.SequenceEqual(TmpVal)) && !alternativs.Any(xs => xs.SequenceEqual(RevVal)))
                {
                    alternativs.Add(TmpVal);
                }
            }
            if (alternativs[0].Contains("011"))
            {
                TmpVal = alternativs[0];
                TmpVal = TmpVal.Replace("011", "100");
                RevVal = string.Concat(Enumerable.Reverse(TmpVal)); //String Reverse

                if (!alternativs.Any(xs => xs.SequenceEqual(TmpVal)) && !alternativs.Any(xs => xs.SequenceEqual(RevVal)))
                {
                    alternativs.Add(TmpVal);
                }

            }

            tempCount = alternativs[0].Count(x => x == '1');
            if (tempCount < arrayCount)
            {
                arrayCount = tempCount;
            }
            alternativs.RemoveAt(0);

        }
        Console.WriteLine(arrayCount);
        sw.Stop();
        Console.WriteLine(sw.Elapsed);
        Console.ReadLine();
    }
}

根据要求,我将完成从原始数组到包含尽可能少的{true} 的数组的过程。在下面的示例中,我将使用1 表示true0 表示false

我将使用一个简单的示例并展示它是如何手动完成的:

这是输入数组{0110100101011},我们称之为myInput。 第 1 步:我选择 1 表单 myInput[1] 并跳过 myInput[2] 并登陆 myInput[3]。这会将myIput[1] 转换为0,将myInput[2] 转换为0,将myInput[3] 转换为1。 此移动的结果数组是{0001100101011}。 如果我将myInput[2] 移动到myInput[1] 上,myInput[2] 将不得不跳转到myInput[0] 并导致像{1000100101011} 这样的数组。这将无法删除myInput[4] 处的1,因为它现在被0 包围。

让我们继续第一个正确的举动,导致{0001100101011}。我要做的下一步是myInput[3]myInut[5]。给我们这个结果{0000010101011}。然后myInput[12]myInput[10]。结果{0000010101100}myInput[10]myInput[8]。结果{0000010110000}myInput[8]myInput[6]。结果{0000011000000}。最后,myInput[6]myInput[4]。结果{0000100000000}。 给我们一个1 数组{0001100101011} 的结果,因为没有更多可能的移动。

由于我编写的程序必须检查所有不同的动作,并且不能说将 1myInput[2] 移动到 myInput[0] 开始是不明智的,所以我必须让程序测试 att不同的动作并将它们存储到我的列表alternativs。这会产生大量场景,这就是减慢我的程序的原因。

我仍然没有设法解决这个问题。

【问题讨论】:

  • 也许我有点睡着了,但我不明白你的问题。您想获得原始数组的组合或排列吗?也许如果你向我们展示你的代码,它会让一切变得更清晰
  • 是的,请向我们展示您的代码。我希望能够复制粘贴并运行。
  • 我在尝试创建场景的地方添加了一些代码。我想从原始数组中获得一个新的修改数组。我正在将所有生成的场景添加到一个列表中,并且该列表正在快速增长!
  • @Billy - 代码无法编译。 alternativs的定义是什么?
  • 我仍然没有得到确切的使用,但如果花费的时间太长,我认为最好预先计算所有的可能性,然后根据你的实际情况你只需要跳转到之前计算的最佳可能性

标签: c# scenarios


【解决方案1】:

我认为您在使用 .Contains 运算符时遇到了问题。

如果你有这个代码:

var list_of_bool_arrays = new List<bool[]>()
{
    new [] { true, false },
};

Console.WriteLine(list_of_bool_arrays.Contains(new [] { true, false }));

...您可能希望它打印True - 很明显数组new [] { true, false } 在数组列表中。但是它打印False。数组是引用类型,仅当引用相同而不是内容时才报告相等性。

要获得True,您需要执行以下代码:

Console.WriteLine(list_of_bool_arrays.Any(xs => xs.SequenceEqual(new [] { true, false })));

所以现在我只是在你的代码中重写了这些行:

if (!alternativs.Any(xs => xs.SequenceEqual(temp)) && !alternativs.Any(xs => xs.SequenceEqual(temp3)))

...和:

if (!alternativs.Any(xs => xs.SequenceEqual(temp2)) && !alternativs.Any(xs => xs.SequenceEqual(temp4)))

然后代码几乎立即运行hardInput,结果为16

【讨论】:

  • 谢谢!我不知道“包含”。但是 hardInput 的输出应该是 6 而不是 16。
  • @Billy - 奇怪的是 lal 也有 16 个。你确定是 6 个吗?
  • 试试这个输入“00101100011010000000000”。
  • @Billy - 这给了我 3 个。
  • @Billy - 我用.Any(xs =&gt; xs.SequenceEqual( 修复重新运行了你的源代码,而我得到的hardInput 的答案又是16。
【解决方案2】:

我还是不明白你想做什么。希望对您有所帮助。

使用另一种方法来加速程序。将所有 true 替换为 1,将所有 false 替换为 0。而不是字节数组将数据存储在字符串中。得到结果easy = 3,hard = 16。

class Program
{
    public static List<string> alternativs = new List<string>();
    public static int arrayCount = 23;
    public static int tempCount = 0;
    static void Main(string[] args)
    {
        //this works fast
        string easyInput = "11000011110000000000000";
        //this takes forever
        string hardInput = "11011011011011011011011";

        string TmpVal = "";
        string RevVal = "";
        bool CanAdd;

        alternativs.Add(hardInput);
        while (alternativs.Count > 0)
        {
            CanAdd = false;
            if (alternativs[0].Contains("110"))
            {
                TmpVal = alternativs[0];
                RevVal = string.Concat(Enumerable.Reverse(TmpVal)); //String Reverse
                TmpVal = TmpVal.Replace("110", "001");
                CanAdd = true;
            }
            if (alternativs[0].Contains("011"))
            {
                TmpVal = alternativs[0];
                RevVal = string.Concat(Enumerable.Reverse(TmpVal)); //String Reverse
                TmpVal = TmpVal.Replace("011", "100");
                CanAdd = true;
            }
            if (CanAdd == true)
            {
                if (!alternativs.Contains(TmpVal) && !alternativs.Contains(RevVal))
                {
                    alternativs.Add(TmpVal);
                }
            }
            tempCount = alternativs[0].Count(x => x == '1');
            if (tempCount < arrayCount)
            {
                arrayCount = tempCount;
            }
            alternativs.RemoveAt(0);

        }
        Console.WriteLine(arrayCount);
        Console.ReadLine();
    }
}

事实证明,如果您需要最少的 true 计数,上面的代码只是平面复杂且不正确。请通过下面的代码...得到的硬结果是8。

class Program
{
    public static string CurStr;
    public static int arrayCount = 23;
    public static int tempCount = 0;
    static void Main(string[] args)
    {
        //this works fast
        string easyInput = "11000011110000000000000";
        //this takes forever
        string hardInput = "11011011011011011011011";

        string TmpVal = "";
        bool CanAdd;

        CurStr = hardInput;
        CanAdd = true;
        while (CanAdd)
        {
            TmpVal = CurStr;
            CanAdd = false;
            if (TmpVal.Contains("110"))
            {
                TmpVal = TmpVal.Replace("110", "001");
                CanAdd = true;
            }
            if (TmpVal.Contains("011"))
            {
                TmpVal = TmpVal.Replace("011", "100");
                CanAdd = true;
            }
            if (CanAdd == true)
            {
                CurStr = TmpVal;
            }
            tempCount = CurStr.Count(x => x == '1');
            if (tempCount < arrayCount)
            {
                arrayCount = tempCount;
            }
        }
        Console.WriteLine(arrayCount);
        Console.ReadLine();
    }
}

【讨论】:

  • 非常感谢您的回复!我将测试您的解决方案并稍微摆弄一下。然而,“硬”输入的正确答案应该是 6 而不是 16。
  • 你能看看下面的第二个代码吗?而且我仍然无法弄清楚你是如何得到 6 的,即使我手动尝试我得到 8。
  • "replace" 方法的问题在于它将所有 110 替换为 001,这可能并不总是最好的做法。有时最好只替换 110 之一,然后用 110 替换 011 之一。我有一个程序可以检查最低答案是什么,问题是它需要很长时间。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多