Everyone Is An Achiever

Sunday, December 14, 2014

loop puzzle - 1

7:07 AM Posted by Unknown 9 comments
Hello everybody, So, do you love solving puzzles? If yes, then before reading the rest of this article give it a go and try to write a loop that will generate the following pattern:

2020202020202020202020202020202020202020 
_19191919191919191919191919191919191919 
__181818181818181818181818181818181818
 ___1717171717171717171717171717171717
 ____16161616161616161616161616161616 
_____151515151515151515151515151515
 ______1414141414141414141414141414
 _______13131313131313131313131313
 ________121212121212121212121212 
_________1111111111111111111111
 __________10101010101010101010
___________999999999 
____________88888888 
_____________7777777 
______________666666 
_______________55555 
________________4444 
_________________333 
__________________22 
___________________1 

Assuming you gave it a go, let us examine how you can solve this puzzle. After the first glance on the pattern, you will realize three things:

  1.  There are 20 rows, starting from 20 going down to 1 
  2.  In each row, the number is repeated as many times as its value( e.g., number 9 is repeated 9 times) 
  3.  From the second row we print an underscore once, then we increase the number of the printed underscores along the way to number 1 


lets kick off with the first point and actually, this is the easiest part. Since we have 20 rows then we will have a loop that will run 20 times.
(for int i = 20;  i >= 1; i—0)
{  
  Console.WriteLine(i);
}
This loop will print the numbers from 20 to 1 single time in each iteration. But what we want is to repeat the number according to its value. To do that, we will have an inner loop that will repeat the number i times.
(for int i = 20;  i >= 1; i—0)
{  
     for (int j = i; j >= 1; j--)
         Console.Write(i);
Console.WriteLine(); // just to print a new line character 
}
To here we achieved point 1 and 2. Lets do the third point which is tricky one. We want the printing of the underscore starts from the second iteration, so the simplest way is to check which iteration is this, if it is not the first iteration then print the underscore. So, our loop will be now like:
for (int i = 20; i >= 1; i--)
            {
                if (i < 20)
                {
                        Console.Write("_");
                }
                for (int j = i; j >= 1; j--)
                    Console.Write(i);
                Console.WriteLine();
            }
However, this will cause the loop to print the underscore one time in every single iteration. What we want is to increase the printing of the underscores. To get that we will apply the following:

  •  Declare two integers: x and counter = 1
  •  Inside the if statement put the following equation: x = i – counter 
  •  Create a third inner loop inside the if statement that will take care of printing the underscores.

for (int k = 1; k <= i- x; k++)
         Console.Write("_");
So the final loop will look like this:
for (int i = 20; i >= 1; i--)
            {
                if (i < 20)
                {
                    x = i - counter;
                    for (int k = 1; k <= i- x; k++)
                        Console.Write("_");
                    counter++;
                }
                for (int j = i; j >= 1; j--)
                    Console.Write(i);

                Console.WriteLine();
            }
Lets analyze the loop a couple of times to understand how it work:

 In the first iteration we will get the number 20 twenty times

 In the second iteration the if block gets executed as: x = 19 – 1; // so x will be 18
 for (int k = 1; k <= i- x; k++) // the condition here is k <= 19 – 18 = 1
 Console.Write("_");
And this will cause the underscore loop to be executed 1 time. Then we increment counter by 1.

In the third execution: x = 18 – 2 // so x will be 16
 for (int k = 1; k <= i- x; k++) // the condition here is k <= 18 – 16 = 2
 Console.Write("_");
And this will cause the underscore loop to be executed 2 times. Then we increment counter by 1.

In the fourth execution: X = 17 – 3 // so x will be 14 for (int k = 1; k <= i- x; k++) // the condition here is k <= 17 – 14 = 3 Console.Write("_"); And this will cause the underscore loop to be executed 3 times. Then we increment counter by 1.

And so on.
That's it. You got the puzzle solved :).
For more convenience, we will replace the 20 number by an N number taken from the user.

Here is the full C# code:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;

namespace TestConsole
{

    class Program
    {
        static void Main(string[] args)
        {
            int n;
            int x = 0, counter = 1;
            Console.WriteLine("Enter the number of rows:");
            n = int.Parse(Console.ReadLine());
            for (int i = n; i >= 1; i--)
            {
                if (i < n)
                {
                    x = i - counter;
                    for (int k = 1; k <= i- x; k++)
                        Console.Write("_");
                    counter++;
                }
                for (int j = i; j >= 1; j--)
                    Console.Write(i);

                Console.WriteLine();
            }

            Console.ReadLine();
        }
    }
}

output:

9 comments:

  1. of course, you can use the power of the .NET framework in generating strings.
    Using some functionality of .NET you can rewrite the loop part like this:
    [C#]
    for (int i = n; i > 0; i--)
    Console.WriteLine( new String('_', 20-i) + String.Concat(Enumerable.Repeat(i.ToString(), i)));

    new String('_', 20-i) generates a string with the specified character (underscore) replicated the specified times.
    [/C#]
    the String.Concat() concatenates multiple values generated by the Enumerable.Repeat(). In this case this generates a list of n times n.ToString().

    All in all a major downsize in code...

    ReplyDelete
  2. Talk about bloat. I say this can be solved in as little as 5 lines.

    ReplyDelete
  3. Of course, Enumerable.Repeat would help you out here indeed, but if you want to stick to loops, you can do it in only two nested loops instead of three loops and an if-statement:

    var line = string.Empty;
    var underscore_buffer = string.Empty;
    for (int nr = 20; nr > 0; nr--)
    {
    for (int i = 0; i < nr; i++)
    {
    line += nr.ToString();
    }
    Console.WriteLine(underscore_buffer + line);
    line = string.Empty;
    underscore_buffer += "_";
    }

    You actually do not have to count the number of underscores, just build 'm up in the last steps of your loop so they will start from the second line. Add one each loop and that problem is solved. The rest is rather easy actually, because it's just a regular descending loop.

    Can't see any practical use for it, but being a puzzle I couldn't resist. Turned out not as hard as I thought it would be :-)

    ReplyDelete
  4. If you don't want to use the framework as described above, then just implement a simple function:
    static void Main(string[] args)
    {
    Console.WriteLine("Enter the number of rows:");
    int num = int.Parse(Console.ReadLine());
    for (int i = 0; i < num; i++)
    {
    Console.Out.WriteLine(Repeat("_", i) + Repeat((num - i).ToString(), num - i));
    }
    Console.ReadLine();
    }

    static string Repeat(string toRepeat, int numRepeats)
    {
    string s = "";
    for (int i = 0; i < numRepeats; i++)
    {
    s += toRepeat;
    }
    return s;
    }

    Or, if you don't want to use functions - just stick with two loops and two counters:
    static void Main(string[] args)
    {
    Console.WriteLine("Enter the number of rows:");
    int num = int.Parse(Console.ReadLine());
    for (int i = 0; i < num; i++)
    {
    for (int j = 0; j < num; j++)
    {
    Console.Write(j < i ? "_" : (num - i).ToString());
    }
    Console.WriteLine();
    }
    Console.ReadLine();
    }

    Shorter and more readable.

    ReplyDelete
  5. Simple

    2 lines of code and a constant

    const int StartNumber = 20;
    private static void Main(string[] args)
    {
    for (int i = StartNumber; i > 0; i--)
    {
    Console.WriteLine(new string('_', StartNumber - i) + new string('*', i).Replace("*", i.ToString()));
    }
    Console.ReadKey();
    }

    ReplyDelete
  6. A one-liner in VB

    Imports System.Text
    Imports System.Linq
    Module Module1

        Sub Main()
            Dim s As String
            For i As Integer = 20 To 1 Step -1
                Console.Out.Write(String.Join("",
                                (Enumerable.Repeat("_", 20 - i)).Concat(
                                    Enumerable.Repeat(i.ToString(), i)
                                ).ToArray()) & System.Environment.NewLine)

            Next
          
            Console.ReadKey()
        End Sub
    End Module

    ReplyDelete
  7. One-liner in Iron Python: for i in range(20, 1, -1): print '_' * (20 - i) + str(i) * i
    Curious to see an F# solution.

    ReplyDelete
  8. Thank you all for your comments. I learned new things from you :)

    ReplyDelete
  9. Here's a one liner:
    static void Main(string[] args)
    {
    Console.WriteLine("Enter the number of rows:");
    int num = int.Parse(Console.ReadLine());
    Enumerable.Range(0, num).Select(i => string.Join("", Enumerable.Range(0, num).Select(j => j < i ? "_" : (num - i).ToString()))).ToList().ForEach(s => Console.WriteLine(s));
    Console.ReadLine();
    }

    ReplyDelete