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:
- There are 20 rows, starting from 20 going down to 1
- In each row, the number is repeated as many times as its value( e.g., number 9 is repeated 9 times)
- 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:
of course, you can use the power of the .NET framework in generating strings.
ReplyDeleteUsing 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...
Talk about bloat. I say this can be solved in as little as 5 lines.
ReplyDeleteOf 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:
ReplyDeletevar 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 :-)
If you don't want to use the framework as described above, then just implement a simple function:
ReplyDeletestatic 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.
Simple
ReplyDelete2 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();
}
A one-liner in VB
ReplyDeleteImports 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
One-liner in Iron Python: for i in range(20, 1, -1): print '_' * (20 - i) + str(i) * i
ReplyDeleteCurious to see an F# solution.
Thank you all for your comments. I learned new things from you :)
ReplyDeleteHere's a one liner:
ReplyDeletestatic 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();
}