0

I have a bool[] and from that original array I want to generate versions/scenarios of the array and save it to a list<bool[]>.

If the array looks something like {true,true,false,true,true,true}. The idea is that if bool[0] and bool[1] both are true and bool[2] is false. I want to create a scenario where bool[0] and bool[1] are false and bool[2] is true and add that scenario to my list. I am currently doing this in a for-loop to check every slot in the array for the pattern {true,true,false} or {false, true,true}. The goal is to find the scenario where I have as few elements of bool = true as possible in the array and return that array.

I am trying to do this with brute force but it is too slow (sometimes minutes). The language I want this to work for is C#. Is there an more efficient way to create the scenarios than with brute force?

My code for generating the scenarios:

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

After some modifications the code looks like this:

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

As requested I will go through the process going from original array to an array with as few {true} as possible. In the example below i will use 1 for true and 0 for false.

I will use a simple example and show how it is done manually:

This is the input array {0110100101011}, let us call it myInput. Step 1: I pick the 1 form myInput[1] and jump over myInput[2] and land in myInput[3]. This will transform myIput[1] to a 0 and myInput[2] to a 0 and myInput[3] to 1. The resulting array from this move is {0001100101011}. If I had moved myInput[2] instead on myInput[1], myInput[2] would have had to jump to myInput[0] and resulting in an array like so {1000100101011}. This would make it impossible to remove the 1 at myInput[4] since it is now surrounded by 0.

Let us continue with the first and correct move which resulted in {0001100101011}. The next move I would make is myInput[3] to myInut[5]. Giving us this result {0000010101011}. Then myInput[12] to myInput[10]. Result {0000010101100}. myInput[10] to myInput[8]. Result {0000010110000}. myInput[8] to myInput[6]. Result {0000011000000}. And finally, myInput[6] to myInput[4]. Result {0000100000000}. Giving us the result of one 1 for the array {0001100101011} since there are no more posible moves.

Since the program I have written have to check all the different moves and can´t tell that it is unwise to start with moving the 1 from myInput[2] to myInput[0] I have to make the program test att the different moves and storing them to my list alternativs. This creates a huge amount of scenarios and that is what is slowing down my program.

I still havent managed to find a solution to this.

Billy
  • 41
  • 7
  • 5
    Maybe i'm a bit asleep yet, but i don't understand your question. Do you want to get combinations or permutations of your original array? Maybe if you show us your code, it would make it all clearer – Pikoh May 02 '16 at 08:50
  • 2
    Yes, please show us your code. I'd like to be able to copy-paste-and-run. – Enigmativity May 02 '16 at 08:51
  • I added the bit of code where I´m trying to create the scenarios. I want to get a new modified array from the original one. I am adding all the generated scenarios to a list and that list is growing fast! – Billy May 02 '16 at 09:00
  • @Billy - The code doesn't compile. What definition of `alternativs`? – Enigmativity May 02 '16 at 09:11
  • @Billy - I'm trying to copy-paste-and-run the code into a console app. Can you please make sure that I can do that with the code you post? – Enigmativity May 02 '16 at 09:12
  • Absolutely! I´ll fix it, hang on. – Billy May 02 '16 at 09:18
  • @Enigmativity - It should work now. I added two different inputs, one easy which works just fine and one hard that takes forever. – Billy May 02 '16 at 09:47
  • Now your code compiles, executes, and I still have no idea of what it is doing. Of course in some cases it would never stop, as you are adding a lot of arrays to `alternativs` in the for loop and only remove one at the end.... – Pikoh May 02 '16 at 09:58
  • @Pikoh - Think of this like a game, If the array looks like this {true,true,false} i want to "jump" with bool[0] "over" bool[1] and end up on position bool[2]. The result would look like this {false,false,true}. So what I want my program to do is find the resulting array with the minumum amount of "true" possible from all different "moves". – Billy May 02 '16 at 10:04
  • 1
    I still don't get exactily the use, but if it is taking too long, i think its better to have all the posibilities pre-calculated and then with your actual situation you'll just have to jump to the best posibility previously calculated – Pikoh May 02 '16 at 11:03
  • @Billy The moment you make a replace you can discard the old value in the alternative since the newly replaced value have less true count. Right? – lal May 03 '16 at 09:40
  • @lal - I don´t think I can do that because even tho the newly created array have less true count an alternative which at that moment have more true might reach a lover true count a new itterations later. But I´m not sure and I will try your idéa! – Billy May 03 '16 at 10:22

2 Answers2

0

I still don't understand what you are trying to do. Hope it helps.

Used an alternative approach to speed up the program. Replaced all the true with 1 and all false to 0. Instead of byte array stored data in string. Got result easy = 3 and 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();
    }
}

It turns out if you need the least count of true, the above code is just plane complicated and incorrect. Please go through the code below... The hard result got is 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();
    }
}
lal
  • 166
  • 4
  • Thank you so much for your response! I will test your solution and fiddle around with it a bit. Hovever, the correct answer for the "hard" input should be 6 and not 16. – Billy May 02 '16 at 11:31
  • Can you please take a look at the 2nd code below. And i still can't figure out how you got 6, even when i try manually i get 8. – lal May 02 '16 at 12:37
  • The problem with the "replace" method is that it replaces all 110 with 001, and that might not always be the best move. Sometimes it is best to just replace one of the 110 and then replace one of the 011 with 110. I have a program that I can check what the lowest answer is, the problem is that it takes forever. – Billy May 02 '16 at 13:02
0

I think you're having a problem with the .Contains operator.

If you have this code:

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

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

...you might expect it to print True - quite clearly the array new [] { true, false } is in the list of the arrays. However it prints False. Arrays are reference types that only report equality when the reference is the same and not the contents.

To get a True you need to do this code:

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

So now I just rewrote those lines in your code:

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

...and:

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

The code then ran hardInput almost instantly with a result of 16.

Enigmativity
  • 113,464
  • 11
  • 89
  • 172
  • Thank you! I didnt know that about "contains". But the output for hardInput should be 6 and not 16. – Billy May 02 '16 at 12:27
  • @Billy - It's odd that lal also got 16. Are you sure it's 6? – Enigmativity May 02 '16 at 13:55
  • Try this input "00101100011010000000000". – Billy May 02 '16 at 14:03
  • @Billy - That gives me 3. – Enigmativity May 02 '16 at 14:05
  • @Billy - I re-ran your source with the `.Any(xs => xs.SequenceEqual(` fix and the answer I got for the `hardInput` was 16 again. – Enigmativity May 02 '16 at 14:06
  • Yes, and it should be 2. – Billy May 02 '16 at 14:06
  • I just ran your code again for `hardInput` and it doesn't call `alternativs.Add(temp);` as `hardInput` is a palindrome and the test `!alternativs.Any(xs => xs.SequenceEqual(temp3))` is always `false`. The second test line has `temp4` always equal to `alternativs[0]` to it doesn't add anything there. The algorithm is then just counting the `true` values in the original input - which is 16. – Enigmativity May 02 '16 at 14:19
  • Well, the answer isnt suppose to be 16 as the least amount of "true" left since the starting amount of "true" is 16. – Billy May 02 '16 at 14:30
  • @Billy - Sorry, that didn't make sense to me. But you can see that the logic of your code never calls `alternativs.Add(temp)` for `hardInput`, right? So then the answer must be 16, right? How did you work it out that it should be 6? – Enigmativity May 02 '16 at 14:44
  • I´ve updated the code a bit, works better now but still cant generate the right answer. – Billy May 02 '16 at 14:53
  • @Billy, as I told you in a previous comment, if you know exactly what the result should be i would go the precompiled results way. Calculate all the possible arrays and results and hardcode them... – Pikoh May 02 '16 at 16:11
  • @Pikoh - I only know the result from my exampels. The ones I use to see if my program actually works. I intend for the program to calculate arrays I do not know the answer to. – Billy May 02 '16 at 18:42
  • @Billy - Your updated code is a significantly different calculation from before. You used to be looping through each index and did a replace only at that location, but now you're potentially doing multiple replacements with each `.Replace` call. I think you need to describe a little more clearly what the algorithm should be. – Enigmativity May 03 '16 at 01:00
  • @Enigmativity - Yes, the new code was created from an answer from @Lal. However, as you mentioned the `.Replace` call makes multiple replacements at a time and that is not always correct. I am just trying all the different ideas that are presented to me. – Billy May 03 '16 at 06:58
  • @Enigmativity - The algorithm? My core problem is the time it takes to create all the "states" the `array` will go through as you perform one move at a time. I managed to create all the states from by doing one move at a time, saving them to a `list`. I can show an example, `{true,true,false,true,true,false}` -> `{false,false,true,true,true,false}` -> `{false,false,true,false,false,true}` & `{false,true,false,false,true,false}`. So we have two answers from this `array` with a result of two "true" left. I want a more efficient way to go to the answer than to generat all the different versions. – Billy May 03 '16 at 07:05
  • @Billy I think your algorithm is a bit complex to solve it just by a loop. Maybe you would need some more advanced techniques of IA, decision trees... – Pikoh May 03 '16 at 09:35
  • @Pikoh - Yes I beleve so. That is what I am looking for. My current method works, but it is super slow. – Billy May 03 '16 at 10:24
  • @Billy - I haven't seen a set of data that is slow yet. Can you provide one? – Enigmativity May 03 '16 at 11:22
  • @Enigmativity - Sure, `{false,true,true,true,true,false,true,true,false,true,true,false,true,true,false,true,true,false,true,true,false,true,true,}` is quite slow. – Billy May 03 '16 at 11:32
  • @Billy - I tried that and got a result in 6ms. How slow is it for you? – Enigmativity May 03 '16 at 11:52
  • @Enigmativity - Wow that was fast! I got the answer in 0.8 s. What result did you get? It should be 4. – Billy May 03 '16 at 11:55
  • @Billy - I got 9. How are you working it out? Have you got code that is doing it? If you're doing it manually can you explain exactly how? Your descriptions so far are too vague. – Enigmativity May 03 '16 at 12:51
  • @Enigmativity - I have a few inputs that I know the result for, I sat for hours doing them manually to have a test set for my program. And I am trying to make a program that can calculate any input I give it. – Billy May 03 '16 at 12:59
  • @Billy - Then teach me how to do it manually. If you can do that then it should be trivial to make a program do it. But right now that hasn't happened. You need to take the time to teach the entire process - don't add a comment - edit your question with the **full detail**. – Enigmativity May 03 '16 at 14:09