Diagonal match in Connect 4 - c#

Hey guys I'm developing a Connect 4 game in Windows form application in C# everything works perfectly I'm just stuck in the diagonal part. This is what I've developed for the left down diagonal check but I'm not sure if it works perfectly. It's working from the first tile which is (1,1) but I can't tell about the other tiles. I also wanted to know how to create another method for the other side diagonal match. (gameButtons is my 2D array and my form is 6 rows and 7 columns) This is my method:
private void checkForDiaMatch()
{
int countBlue = 0;
int countRed = 0;
for (int i = 0; i < 6; i++)
{
if (gameButtons[i, i].BackColor == Color.Blue)
{
countBlue++;
}
else
{
countBlue = 0;
}
if (gameButtons[i, i].BackColor == Color.Red)
{
countRed++;
}
else
{
countRed = 0;
}
if (countBlue >= 4)
{
MessageBox.Show("There is a blue diagonal match");
MessageBox.Show("Blue wins!");
}
else if (countRed >= 4)
{
MessageBox.Show("There is a red diagonal match");
MessageBox.Show("Red wins!");
}
}
}
Edit: So since I took some help from the comment section I created this but it's still not working. I tried going for the diagonal right match but no luck yet.
private void checkForDiaMatch(int col,int targetRow)
{
int countBlue = 0;
int countRed = 0;
int xLocation = gameButtons[col, targetRow].Location.X/50;
int yLocation = gameButtons[col, targetRow].Location.Y/50;
//string epop = Convert.ToString(xLocation);
//MessageBox.Show(epop);
if (7 > xLocation + 1 && 6 > yLocation + 1)
{
if (gameButtons[xLocation + 1, yLocation + 1].BackColor == Color.Blue)
{
countBlue++;
string kappaBlue = Convert.ToString(countBlue);
MessageBox.Show(kappaBlue);
}
else
{
countBlue = 0;
}
if (gameButtons[xLocation + 1, yLocation + 1].BackColor == Color.Red)
{
countRed++;
string kappaRed = Convert.ToString(countRed);
MessageBox.Show(kappaRed);
}
else
{
countRed = 0;
}

Probably the most helpful thing would be to draw your board and indexes on paper, and write the indexes of any random diagonal connect 4:
------------------------------- -------------------------------
5 | | | | | | |5,6| 5 | | | | | | | |
--+---+---+---+---+---+---+---+ --+---+---+---+---+---+---+---+
4 | | | | | |4,5| | 4 | |4,1| | | | | |
--+---+---+---+---+---+---+---+ --+---+---+---+---+---+---+---+
3 | | | | |3,4| | | 3 | | |3,2| | | | |
--+---+---+---+---+---+---+---+ --+---+---+---+---+---+---+---+
2 | | | |2,3| | | | 2 | | | |2,3| | | |
--+---+---+---+---+---+---+---+ --+---+---+---+---+---+---+---+
1 | | | | | | | | 1 | | | | |1,4| | |
--+---+---+---+---+---+---+---+ --+---+---+---+---+---+---+---+
0 | | | | | | | | 0 | | | | | | | |
--+---+---+---+---+---+---+---+ --+---+---+---+---+---+---+---+
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
We can see that from any point we need to check in two directions: up/right + down/left (like a /), and up/left + down/right (like a \). We want to count in both these directions the number of same-colored pieces as the one that we started from. As soon as the total number is 4, we can return true. If neither directions has a total of 4 matching pieces, we return false.
I played with this a little, and came up with the following method that works for me. Your classes are probably slightly different (I just did a console app), but hopefully the logic will help:
private bool CompletesDiagonal(int pieceRow, int pieceCol)
{
var colorToMatch = Board[pieceRow, pieceCol]; // Board is a ConsoleColor[7,6] array
var matchingPieces = 1; // We will count the original piece as a match
// Check forward slash direction '/'
// First check down/left (decrement both row and column up to 3 times)
for (int counter = 1; counter < 4; counter++)
{
var row = pieceRow - counter;
var col = pieceCol - counter;
// Make sure we stay within our board
if (row < Board.GetLowerBound(0) || col < Board.GetLowerBound(1)) { break; }
if (Board[row, col] == colorToMatch)
{
matchingPieces++;
if (matchingPieces == 4) return true;
}
else { break; }
}
// Next check up/right (increment both row and column up to 3 times)
for (int counter = 1; counter < 4; counter++)
{
var row = pieceRow + counter;
var col = pieceCol + counter;
// Make sure we stay within our board
if (row > Board.GetUpperBound(0) || col > Board.GetUpperBound(1)) { break; }
// Check for a match
if (Board[row, col] == colorToMatch)
{
matchingPieces++;
if (matchingPieces == 4) return true;
}
else { break; }
}
// If we got this far, no match was found in forward slash direction,
// so reset our counter and check the back slash direction '\'
matchingPieces = 1;
// First check down/right (decrement row and increment column)
for (int counter = 1; counter < 4; counter++)
{
var row = pieceRow - counter;
var col = pieceCol + counter;
// Make sure we stay within our board
if (row < Board.GetLowerBound(0) || col > Board.GetUpperBound(1)) { break; }
// Check for a match
if (Board[row, col] == colorToMatch)
{
matchingPieces++;
if (matchingPieces == 4) return true;
}
else { break; }
}
// Next check up/left (increment row and decrement column)
for (int counter = 1; counter < 4; counter++)
{
var row = pieceRow + counter;
var col = pieceCol - counter;
// Make sure we stay within our board
if (row > Board.GetUpperBound(0) || col < Board.GetLowerBound(1)) { break; }
// Check for a match
if (Board[row, col] == colorToMatch)
{
matchingPieces++;
if (matchingPieces == 4) return true;
}
else { break; }
}
// If we've gotten this far, then we haven't found a match
return false;
}
Here's the result of a diagonal win:

Related

Convert string to number without built in functions

I'm told to implement fucntion which takes string as a parameter and returns int. Here is how i implemented, but the question is that my implementation is ugly and would like to see other implementations of this function. According to conditions you're not allowed to use built in fucntions such tryParse, parse, or something what does all the job for you.
My implementation:
private static int StringToNumber(string str)
{
int number = 0;
if (str.Contains('-'))
{
foreach (var character in str)
{
if (character == '-')
{
continue;
}
number += character - '0';
number *= 10;
}
number *= (-1);
number /= 10;
}
else
{
foreach (var character in str)
{
number *= 10;
number += character - '0';
}
}
return number;
}
Here is a variant combining OP's char subtraction solution and #sitholewb solution, that is somewhat optimized.
public static int StringToIntCharSubtraction(string str)
{
if (string.IsNullOrWhiteSpace(str))
{
//invalid input, do something
return 0;
}
var num = 0;
var sign = 1;
int i = 0;
if (str[0] == '-')
{
sign = -1;
i = 1;
}
while (i < str.Length)
{
int currentNum = (str[i] - '0');
if (currentNum > 9 || currentNum < 0)
{
//do something else or ignore
continue;
}
num = (num * 10) + currentNum;
i++;
}
return num * sign;
}
If you are worried about performance here is benchmark.
| Method | number | Mean | Error | StdDev | Ratio | Rank | Allocated |
|----------------------------- |-------- |----------:|----------:|----------:|------:|-----:|----------:|
| StringToIntCharSubtraction | 220567 | 6.310 ns | 0.0637 ns | 0.0565 ns | 0.44 | 1 | - |
| StringToIntSwitch | 220567 | 13.824 ns | 0.3083 ns | 0.2884 ns | 0.96 | 2 | - |
| int.Parse | 220567 | 14.345 ns | 0.0883 ns | 0.0782 ns | 1.00 | 3 | - |
| | | | | | | | |
| StringToIntCharSubtraction | -829304 | 6.413 ns | 0.0556 ns | 0.0492 ns | 0.45 | 1 | - |
| StringToIntSwitch | -829304 | 12.896 ns | 0.2711 ns | 0.2784 ns | 0.90 | 2 | - |
| int.Parse | -829304 | 14.272 ns | 0.2637 ns | 0.2467 ns | 1.00 | 3 | - |
You can even drop the first one to 3 ns if you remove the validations, but it seems too risky for me.
You can use the approach following approach to solve this as well.
private static int StringToInt(string str)
{
if (string.IsNullOrWhiteSpace(str) || str.Length == 0)
{
//invalid input, do something
return 0;
}
var num = 0;
var sign = 1;
if (str[0] == '-')
{
sign = -1;
str = str.Substring(1);
}
foreach (var c in str)
{
switch (c)
{
case '0':
num = (num * 10);
break;
case '1':
num = (num * 10) + 1;
break;
case '2':
num = (num * 10) + 2;
break;
case '3':
num = (num * 10) + 3;
break;
case '4':
num = (num * 10) + 4;
break;
case '5':
num = (num * 10) + 5;
break;
case '6':
num = (num * 10) + 6;
break;
case '7':
num = (num * 10) + 7;
break;
case '8':
num = (num * 10) + 8;
break;
case '9':
num = (num * 10) + 9;
break;
default:
//do something else or ignore
break;
}
}
return num * sign;
}

Why does this double-ended selectionsort algorithm not work?

I just wrote a simple algorithm to sort integer-values in an array. When I try to debug it, it sometimes works and sometimes it does not. I really don't know why. Can anyone help me?
This is my code:
void swap(int[] array, int left, int right)
{
int tmp = array[left];
array[left] = array[right];
array[right] = tmp;
}
void selectionsort(int[] givenArray)
{
int maxindex = givenArray.Length - 1;
for (int border = 0; border < givenArray.Length / 2; border++)
{
for (int i = 0; i < givenArray.Length - (border * 2); i++)
{
if (i != 0)
{
if (givenArray[border + i] < givenArray[border])
swap(givenArray, border + i, border);
if (givenArray[border + i] > givenArray[maxindex - border])
swap(givenArray, border + i, maxindex - border);
}
}
}
}
Edit: I usually generate 10 random numbers between 1 and 9999 and print them out. Then I start the sorting and then I print them out again. Here is the output:
[Random] >> 2257 | 2866 | 8796 | 4497 | 4697 | 2393 | 1004 | 4799 | 483 | 8500 |
[Sorted] >> 483 | 2257 | 1004 | 2866 | 2393 | 4497 | 4697 | 4799 | 8500 | 8796 |
Sorting failed at 2/3
sorting failed at 4/5
Array has been sorted in less than 1 ms
I updated your example to print the array before every iteration (I also adjusted the inner loop to not pointlessly start at 0):
public static void selectionsort(int[] givenArray)
{
int maxindex = givenArray.Length - 1;
for (int border = 0; border < givenArray.Length / 2; border++)
{
PrintArray(givenArray);
for (int i = 1; i < givenArray.Length - (border * 2); i++)
{
if (givenArray[border + i] < givenArray[border])
swap(givenArray, border + i, border);
if (givenArray[border + i] > givenArray[maxindex - border])
swap(givenArray, border + i, maxindex - border);
}
}
}
public static void PrintArray(int[] givenArray)
{
var first = true;
for (int i = 0; i < givenArray.Length; i++)
{
if(!first) Console.Write(" | ");
Console.Write(givenArray[i]);
first = false;
}
Console.WriteLine();
}
And see this output:
2257 | 2866 | 8796 | 4497 | 4697 | 2393 | 1004 | 4799 | 483 | 8500
483 | 2866 | 8500 | 4497 | 4697 | 2393 | 2257 | 4799 | 1004 | 8796
483 | 2257 | 1004 | 4497 | 4697 | 2866 | 2393 | 4799 | 8500 | 8796
483 | 2257 | 1004 | 4497 | 4697 | 2866 | 2393 | 4799 | 8500 | 8796
483 | 2257 | 1004 | 2866 | 2393 | 4497 | 4697 | 4799 | 8500 | 8796
483 | 2257 | 1004 | 2866 | 2393 | 4497 | 4697 | 4799 | 8500 | 8796
We can see after the first iteration that the second lowest value has moved into the last-but-one position in the array.
During the next iteration, we're trying to place the items in position 1 and position 8 (0-based indexing). But the items in those two positions are never directly compared with each other.
One fix would be to compare givenArray[border] to givenArray[maxindex - border] before your inner loop and swap those two items if necessary.
public static void selectionsort(int[] givenArray)
{
int maxindex = givenArray.Length - 1;
for (int border = 0; border < givenArray.Length / 2; border++)
{
if(givenArray[border] > givenArray[maxindex - border])
swap(givenArray, border, maxindex - border);
for (int i = 1; i < givenArray.Length - (border * 2); i++)
{
if (givenArray[border + i] < givenArray[border])
swap(givenArray, border + i, border);
if (givenArray[border + i] > givenArray[maxindex - border])
swap(givenArray, border + i, maxindex - border);
}
}
}

Why my use of Intrinsics API is slower than naive solution?

I struggle to realize why my usage of intrinsics API is slower than just sum with foreach loop?
public class ArraySum
{
private double[] data;
public ArraySum()
{
if (!Avx.IsSupported)
{
throw new Exception("Avx is not supported");
}
var rnd = new Random();
var list = new List<double>();
for (int i = 0; i < 100_000; i++)
{
list.Add(rnd.Next(500));
}
data = list.ToArray();
}
[Benchmark]
public void Native()
{
int result = 0;
foreach (int i in data)
{
result += i;
}
Console.WriteLine($"Native: {result}");
}
[Benchmark]
public unsafe void Intrinsics()
{
int vectorSize = 256 / 8 / 4;
var accVector = Vector256<double>.Zero;
int i;
var array = data;
fixed (double* ptr = array)
{
for (i = 0; i <= array.Length - vectorSize; i += vectorSize)
{
var v = Avx.LoadVector256(ptr + i);
accVector = Avx.Add(accVector, v);
}
}
double result = 0;
var temp = stackalloc double[vectorSize];
Avx.Store(temp, accVector);
for (int j = 0; j < vectorSize; j++)
{
result += temp[j];
}
for (; i < array.Length; i++)
{
result += array[i];
}
Console.WriteLine($"Intrinsics: {result}");
}
Result:
.NET SDK=6.0.100-rc.2.21505.57
| Method | Mean | Error | StdDev | Median |
|----------- |---------:|---------:|---------:|---------:|
| Native | 387.6 us | 12.15 us | 35.83 us | 405.8 us |
| Intrinsics | 393.2 us | 9.01 us | 25.70 us | 385.0 us |
what may be causing this?
It's running on Windows and Intel Core i5-3340M CPU 2.70GHz (Ivy Bridge) if it does matter
BenchmarkDotNet warns that ArraySum.Native: Default -> It seems that the distribution is bimodal (mValue = 3.92)
I just realized that
native method should perform it on doubles not ints, opsie
[Benchmark]
public void Native()
{
double result = 0;
foreach (double i in data)
{
result += i;
}
Console.WriteLine($"Native: {result}");
}
| Method | Mean | Error | StdDev | Median |
|----------- |---------:|---------:|---------:|---------:|
| Native | 415.1 us | 25.35 us | 73.95 us | 385.9 us |
| Intrinsics | 388.7 us | 7.58 us | 21.74 us | 384.7 us |
but also:
Console.WriteLine adds probably too much overhead which is way higher than time spent performing sum and skews the results
now the difference is more significant:
[Benchmark]
public double Native()
{
double result = 0;
foreach (double i in data)
{
result += i;
}
return result;
}
[Benchmark]
public unsafe double Intrinsics()
{
int vectorSize = 256 / 8 / 4;
var accVector = Vector256<double>.Zero;
int i;
var array = data;
fixed (double* ptr = array)
{
for (i = 0; i <= array.Length - vectorSize; i += vectorSize)
{
var v = Avx.LoadVector256(ptr + i);
accVector = Avx.Add(accVector, v);
}
}
double result = 0;
var temp = stackalloc double[vectorSize];
Avx.Store(temp, accVector);
for (int j = 0; j < vectorSize; j++)
{
result += temp[j];
}
for (; i < array.Length; i++)
{
result += array[i];
}
return result;
}
| Method | Mean | Error | StdDev |
|----------- |---------:|---------:|---------:|
| Native | 92.92 us | 1.547 us | 1.447 us |
| Intrinsics | 25.06 us | 0.459 us | 1.090 us |

A local function named 's' is already declared... Why am I prompted this everytime I change the variable in the for loop?

I have a scenario where the user will input a number that will be the multiplier for an image (the image isn't from a file it's just text on the console using special characters). It could be thought of as ASCII art but I didn't go the route of using ASCII values for this scenario. I instead am using multiple strings, and I want to only multiply specific characters within each string, where it will match the original scale of the original 'image', but bigger depending on what the user picks.
I've tried going down the ASCII route, but my code was pretty messy. I tried to make each line of the image a separate method, and call each of them through a parameter, but never got to the parameter part because I got confused on how I could change the specific characters like stated above.
static void Main (string[] args)
{
string edgeBorder = "#================#";
int multiplier;
multiplier = Int32.Parse(Console.ReadLine());
Console.WriteLine(multiplier);
Console.WriteLine("Sure! Coming right up...");
//Top layer of quilt
for (int i = 0; i < multiplier; i++)
{
Console.Write(edgeBorder + " ");
}
Console.Write("\n");
//top half of quilt
for (int line = 1; line <= 4; line++)
{
for (int s = 0, s < (8 - 2 * line) * multiplier; s++)
{ //s for space
Console.Write(" ");
}
Console.Write("|" + " ");
if (line == 1)
{
for (int d = 0; d < 2 * multiplier; d++)
{ //d for diamond
Console.Write("<>");
}
}
else
{
Console.Write("<>");
for (int p = 0; p < 4 * multiplier * (line - 1); p++)
{ //p for period
Console.Write(".");
}
Console.Write("<>");
}
Console.Write(" " + "|");
for (int s = 0, s < (8 - 2 * line) * multiplier; s++)
{ //s for space
Console.Write(" ");
}
Console.Write("\n");
}
//bottom half of quilt
for (int line = 4; line >= 1; line--)
{
for (int s = 0, s < (8 - 2 * line) * multiplier; s++)
{ //s for space
Console.Write(" ");
}
Console.Write("|" + " ");
if (line == 1)
{
for (int d = 0; d < 2 * multiplier; d++)
{ //d for diamond
Console.Write("<>");
}
}
else
{
Console.Write("<>");
for (int p = 0; p < 4 * multiplier * (line - 1); p++)
{ //p for period
Console.Write(".");
}
Console.Write("<>");
}
Console.Write(" " + "|");
for (int s = 0, s < (8 - 2 * line) * multiplier; s++)
{ //s for space
Console.Write(" ");
}
Console.Write("\n");
}
//bottom layer of quilt
for (int i = 0; i < multiplier; i++)
{
Console.Write(edgeBorder + " ");
}
}
//I went a different route, and decided not to use an array, only issue is it
//keeps telling me 's' is already defined in the scope, but when I change it to
//something different it says the same exact thing
/*$================$
| <><> |
| <>....<> |
| <>........<> |
|<>............<>|
|<>............<>|
| <>........<> |
| <>....<> |
| <><> |
$================$
and be this if multiplied by 2
$================$ $================$
| <><><><> |
| <>........<> |
| <>................<> |
|<>........................<>|
|<>........................<>|
| <>................<> |
| <>........<> |
| <><><><> |
$================$ #================$
I tried putting this into the "what's expected but it wouldn't format properly*/
I'm expecting an 'image' to be shown in the console, that's at whatever scale the user wants it to be. I'm lost on how to implement my idea to the code.
I put the expected part in the code section, it wouldn't format properly.
A. Inflate
Algorithm
image_width = firstLine.Lenght;
for each line:
if( first or last)
repeat with a space in between
else
pad right until width == image_width
repeat each air molecule (the dot) twice
( pad left, pad right ) until width == image_width
Code
private static IEnumerable<string> Inflate(string[] lines, int scale, string air)
{
// image_width = firstLine.Lenght;
// for each line:
// if( first or last)
// repeat with a space in between
// else
// pad right until width == image_width
// repeat each air molecule (the dot) twice
// ( pad left, pad right ) until width == image_width
var imageWidth = lines[0].TrimEnd().Length;
return lines.Select((line, i) =>{
if (i == 0 || i == lines.Length - 1)
return string.Join("", Enumerable.Repeat(line.TrimEnd(), scale));
line = line.PadRight(imageWidth, ' ');
line = line.Replace(air, string.Join("", Enumerable.Repeat(air, scale)));
while (line.Length < imageWidth * scale) line = " " + line + " ";
return line;
});
}
private static string Inflate(string input, int scale, string air)
=> string.Join(Environment.NewLine, Inflate(
input.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries), scale, air));
Test code
// I Maually removed '/*' from the input
// Please note that the first line ends with a space,
// but this space trimmed and is not considered part of the image
string input = #"$================$
| <><> |
| <>....<> |
| <>........<> |
|<>............<>|
|<>............<>|
| <>........<> |
| <>....<> |
| <><> |
$================$";
Console.WriteLine(input);
Console.WriteLine(Inflate(input, air: ".", scale: 2));
Console.WriteLine(Inflate(input, air: ".", scale: 3));
Console.WriteLine(Inflate(input, air: ".", scale: 4));
Output
Notes:
The images don't contain the spaces between the repeated header and footer, and your example output has spaces, but these spaces make the images asymmetrical, so I didn't includ them.
Your test image changes | <><> | to | <><><><> |, and this alogorithm doesn't. It could be modified to do that. It could also be extended to not have gaps so that air (.) doesn't escape, but that's for another day.
$================$
| <><> |
| <>....<> |
| <>........<> |
|<>............<>|
|<>............<>|
| <>........<> |
| <>....<> |
| <><> |
$================$
$================$$================$
| <><> |
| <>........<> |
| <>................<> |
|<>........................<>|
|<>........................<>|
| <>................<> |
| <>........<> |
| <><> |
$================$$================$
$================$$================$$================$
| <><> |
| <>............<> |
| <>........................<> |
|<>....................................<>|
|<>....................................<>|
| <>........................<> |
| <>............<> |
| <><> |
$================$$================$$================$
$================$$================$$================$$================$
| <><> |
| <>................<> |
| <>................................<> |
|<>................................................<>|
|<>................................................<>|
| <>................................<> |
| <>................<> |
| <><> |
$================$$================$$================$$================$
B. Multiply the atoms
This is not exactly what you want, but it may be good enough.
var output = string.Join(Environment.NewLine,
lines.Select( l => new string(l.SelectMany(ch => Enumerable.Repeat(ch, scale)).ToArray())));
Test
Code
string input = #"/*$================$
| <><> |
| <>....<> |
| <>........<> |
|<>............<>|
|<>............<>|
| <>........<> |
| <>....<> |
| <><> |
$================$";
Console.WriteLine(input);
var lines = input.Split(new [] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
int scale = 2;
Console.WriteLine("");
Console.WriteLine($"Scale : {scale}");
Console.WriteLine("");
var newLines =
lines.Select( l => new string(l.SelectMany(ch => Enumerable.Repeat(ch, scale)).ToArray()));
var output = string.Join(Environment.NewLine,newLines);
Console.WriteLine(output);
Output
/*$================$
| <><> |
| <>....<> |
| <>........<> |
|<>............<>|
|<>............<>|
| <>........<> |
| <>....<> |
| <><> |
$================$
Scale : 2
//**$$================================$$
|| <<>><<>> ||
|| <<>>........<<>> ||
|| <<>>................<<>> ||
||<<>>........................<<>>||
||<<>>........................<<>>||
|| <<>>................<<>> ||
|| <<>>........<<>> ||
|| <<>><<>> ||
$$================================$$

More efficient algorithm for printing numbers that are palindromic and their power to 2 are palindromic too

I am looking for more efficient algorithm for printing numbers that are palindromic (for example 1001) and their power to 2 (1001 * 1001 = 1002001) are palindromic too. In my algorithm I think I make unnecessary checks to determine if number is palindromic. How can I improve it?
In [1000,9999] range I found this kind of 3 numbers: 1001, 1111 and 2002.
This is my algorithm:
for (int i = n; i <= m; i++)
{
if (checkIfPalindromic(i.ToString()))
{
if (checkIfPalindromic((i * i).ToString()))
Console.WriteLine(i);
}
}
this is my method to determine if number is palindromic:
static bool checkIfPalindromic(string A)
{
int n = A.Length - 1;
int i = 0;
bool IsPalindromic = true;
while (i < (n - i))
{
if (A[i] != A[n - i])
{
IsPalindromic = false;
break;
}
i++;
}
return IsPalindromic;
}
Instead of checking very number for "palindromness", it may be better to iterate through palindromes only. For that just iterate over the first halves of the number and then compose palindrome from it.
for(int half=10;half<=99;++half)
{
const int candidate=half*100+Reverse(half);//may need modification for odd number of digits
if(IsPalindrome(candidate*candidate))
Output(candidate);
}
This will make your program O(sqrt(m)) instead of O(m), which will probably beat all improvements of constant factors.
What you have already seems fairly efficient
Scale is checking 1,000,000 integers
Note : i use longs
Disclaimer : I must admit these results are a little sketchy, ive added more scaling so you can see
Results
Mode : Release
Test Framework : .Net 4.7.1
Benchmarks runs : 10 times (averaged)
Scale : 1,000
Name | Average | Fastest | StDv | Cycles | Pass | Gain
-----------------------------------------------------------------
Mine2 | 0.107 ms | 0.102 ms | 0.01 | 358,770 | Yes | 5.83 %
Original | 0.114 ms | 0.098 ms | 0.05 | 361,810 | Base | 0.00 %
Mine | 0.120 ms | 0.100 ms | 0.03 | 399,935 | Yes | -5.36 %
Scale : 10,000
Name | Average | Fastest | StDv | Cycles | Pass | Gain
-------------------------------------------------------------------
Mine2 | 1.042 ms | 0.944 ms | 0.17 | 3,526,050 | Yes | 11.69 %
Mine | 1.073 ms | 0.936 ms | 0.19 | 3,633,369 | Yes | 9.06 %
Original | 1.180 ms | 0.920 ms | 0.29 | 3,964,418 | Base | 0.00 %
Scale : 100,000
Name | Average | Fastest | StDv | Cycles | Pass | Gain
--------------------------------------------------------------------
Mine2 | 10.406 ms | 9.502 ms | 0.91 | 35,341,208 | Yes | 6.59 %
Mine | 10.479 ms | 9.332 ms | 1.09 | 35,592,718 | Yes | 5.93 %
Original | 11.140 ms | 9.272 ms | 1.72 | 37,624,494 | Base | 0.00 %
Scale : 1,000,000
Name | Average | Fastest | StDv | Cycles | Pass | Gain
-------------------------------------------------------------------------
Original | 106.271 ms | 101.662 ms | 3.61 | 360,996,200 | Base | 0.00 %
Mine | 107.559 ms | 102.695 ms | 5.35 | 365,525,239 | Yes | -1.21 %
Mine2 | 108.757 ms | 104.530 ms | 4.81 | 368,939,992 | Yes | -2.34 %
Mode : Release
Test Framework : .Net Core 2.0
Benchmarks runs : 10 times (averaged)
Scale : 1,000,000
Name | Average | Fastest | StDv | Cycles | Pass | Gain
-------------------------------------------------------------------------
Mine2 | 95.054 ms | 87.144 ms | 8.45 | 322,650,489 | Yes | 10.54 %
Mine | 95.849 ms | 89.971 ms | 5.38 | 325,315,589 | Yes | 9.79 %
Original | 106.251 ms | 84.833 ms | 17.97 | 350,106,144 | Base | 0.00 %
Given
protected override List<int> InternalRun()
{
var results = new List<int>();
for (var i = 0; i <= Input; i++)
if (checkIfPalindromic(i) && checkIfPalindromic(i * (long)i))
results.Add(i);
return results;
}
Mine1
private static unsafe bool checkIfPalindromic(long value)
{
var str = value.ToString();
fixed (char* pStr = str)
{
for (char* p = pStr, p2 = pStr + str.Length - 1; p < p2;)
if (*p++ != *p2--)
return false;
}
return true;
}
Mine2
private static bool checkIfPalindromic(long value)
{
var str = value.ToString();
var n = str.Length - 1;
for (var i = 0; i < n - i; i++)
if (str[i] != str[n - i])
return false;
return true;
}
More optimistic way is to use int instead of string. this algorithm is about two time faster:
static int[] pow10 = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
static bool checkIfPalindromic(int A)
{
int n = 1;
int i = A;
if (i >= 100000000) { n += 8; i /= 100000000; }
if (i >= 10000) { n += 4; i /= 10000; }
if (i >= 100) { n += 2; i /= 100; }
if (i >= 10) { n++; }
int num = A / pow10[(n+1) / 2];
for (; num % 10 == 0;)
num /= 10;
int reversedNum = 0;
for (int input = A % pow10[ n / 2]; input != 0; input /= 10)
reversedNum = reversedNum * 10 + input % 10;
return num == reversedNum;
}
Usage:
for (int i = n; i <= m; i++)
if (checkIfPalindromic(i) && checkIfPalindromic(i * i))
Console.WriteLine(i);
Benchmark:
Bemchmark in range of [1000, 99999999] on Core2Duo CPU:
This algorithm: 12261ms
Your algorithm: 24181ms
Palindromic Numbers:
1001
1111
2002
10001
10101
10201
11011
11111
11211
20002
20102
you can use Linq to simplify your code
sample:-
static void Main(string[] args)
{
int n = 1000, m = 9999;
for (int i = n; i <= m; i++)
{
if (CheckIfNoAndPowerPalindromic(i))
{
Console.WriteLine(i);
}
}
}
private static bool CheckIfNoAndPowerPalindromic(int number)
{
string numberString = number.ToString();
string numberSquareString = (number * number).ToString();
return (Enumerable.SequenceEqual(numberString.ToCharArray(), numberString.ToCharArray().Reverse()) &&
Enumerable.SequenceEqual(numberSquareString.ToCharArray(), numberSquareString.ToCharArray().Reverse()));
}
output:-
1001
1111
2002.
Loop up to len/2 as follow:
static bool checkIfPalindromic(string A)
{
for (int i = 0; i < A.Length / 2; i++)
if (A[i] != A[A.Length - i - 1])
return false;
return true;
}
We can get an interesting optimisation by changing the palindromic checking method and using a direct integer reversing method instead of converting first to a string then looping in the string.
I used the method in the accepted answer from this question:
static int reverse(int n)
{
int left = n;
int rev = 0;
int r = 0;
while (left > 0)
{
r = left % 10;
rev = rev * 10 + r;
left = left / 10;
}
return rev;
}
I also used the StopWatch from System.Diagnostics to measure the elapsed time.
My function to check if a number is a palindromic number is:
static bool IsPalindromicNumber(int number)
{
return reverse(number) == number;
}
For n value of 1000 and for different values of m I get the following results for the elapsed time in milliseconds:
---------------------------------------------------------
| m | original | mine | optimisation|
---------------------------------------------------------
|9999 |6.3855 |4.2171 | -33.95% |
---------------------------------------------------------
|99999 |71.3961 |42.3399 | -40.69% |
---------------------------------------------------------
|999999 |524.4921 |342.8899 | -34.62% |
---------------------------------------------------------
|9999999 |7016.4050 |4565.4563 | -34.93% |
---------------------------------------------------------
|99999999 |71319.658 |49837.5632 | -30.12% |
---------------------------------------------------------
The measured values are an indicative and not absolute because from one run of the program to another they are different but the pattern stays the same and the second approach appears always faster.
To measure using the StopWatch:
With your method:
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
for (int i = n; i <= m; i++)
{
if (checkIfPalindromic(i.ToString()))
{
if (checkIfPalindromic((i * i).ToString()))
Console.WriteLine(i);
}
}
stopWatch.Stop();
Console.WriteLine("First approach: Elapsed time..." + stopWatch.Elapsed + " which is " + stopWatch.Elapsed.TotalMilliseconds + " miliseconds");
I used of course exact same approach with my changes:
With my method:
Stopwatch stopWatch2 = new Stopwatch();
stopWatch2.Start();
for (int i = n; i <= m; i++)
{
if (IsPalindromicNumber(i) && IsPalindromicNumber(i*i))
{
Console.WriteLine(i);
}
}
stopWatch2.Stop();
Console.WriteLine("Second approach: Elapsed time..." + stopWatch2.Elapsed + " which is " + stopWatch2.Elapsed.TotalMilliseconds + " miliseconds");

Categories

Resources