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;
}
Related
Hi I have the following code:
public unsafe class MultiplyAndAdd : IDisposable
{
float[] rawFirstData = new float[1024];
float[] rawSecondData = new float[1024];
static int alignment = 32;
float[] alignedFirstData = new float[1024 + alignment / sizeof(float)];
int alignedFirstDataOffset;
GCHandle alignedFirstDataHandle;
float* alignedFirstDataPointer;
float[] alignedSecondData = new float[1024 + alignment / sizeof(float)];
int alignedSecondDataOffset;
GCHandle alignedSecondDataHandle;
float* alignedSecondDataPointer;
public IEnumerable<object[]> Data { get; set; }
public void Dispose()
{
this.alignedFirstDataHandle.Free();
this.alignedSecondDataHandle.Free();
}
//Calculate the offset that needs to be applied to ensure that the array is aligned with 32.
private int CalculateAlignmentOffset(GCHandle handle)
{
var handlePointer = handle.AddrOfPinnedObject().ToInt64();
long lPtr2 = (handlePointer + alignment - 1) & ~(alignment - 1);
return (int)(lPtr2 - handlePointer);
}
public MultiplyAndAdd()
{
Random random = new Random(1055);
for (var i = 0; i < 1024; i++)
{
rawFirstData[i] = (float)random.NextDouble() * 4f - 2f;
rawSecondData[i] = (float)random.NextDouble() * 4f - 2f;
}
alignedFirstDataHandle = GCHandle.Alloc(alignedFirstData, GCHandleType.Pinned);
alignedFirstDataOffset = CalculateAlignmentOffset(alignedFirstDataHandle);
alignedFirstDataPointer = (float*)(alignedFirstDataHandle.AddrOfPinnedObject() + alignedFirstDataOffset);
alignedSecondDataHandle = GCHandle.Alloc(alignedSecondData, GCHandleType.Pinned);
alignedSecondDataOffset = CalculateAlignmentOffset(alignedSecondDataHandle);
alignedSecondDataPointer = (float*)(alignedSecondDataHandle.AddrOfPinnedObject() + alignedSecondDataOffset);
for (var i = 0; i < 1024; i++)
{
alignedFirstData[i + alignedFirstDataOffset / sizeof(float)] = rawFirstData[i];
alignedSecondData[i + alignedSecondDataOffset / sizeof(float)] = rawSecondData[i];
}
Data = new[] {
//7,
8,
//11,
//16,
20,
//30,
32,
//40,
50 }.Select(x => new object[] { x }).ToList();
}
public void Validate()
{
for(var i = 0; i < 1024; i++)
{
if (rawFirstData[i] != alignedFirstData[i + alignedFirstDataOffset / sizeof(float)])
{
throw new InvalidOperationException("Diff found!");
}
if (rawFirstData[i] != *(alignedFirstDataPointer + i))
{
throw new InvalidOperationException("Diff found!");
}
if (rawSecondData[i] != alignedSecondData[i + alignedSecondDataOffset / sizeof(float)])
{
throw new InvalidOperationException("Diff found!");
}
if (rawSecondData[i] != *(alignedSecondDataPointer + i))
{
throw new InvalidOperationException("Diff found!");
}
}
Action<string, float, float> ensureAlmostSame = delegate (string name, float normal, float other)
{
var diff = MathF.Abs(normal - other);
if (diff > 0.00001)
{
throw new InvalidOperationException($"The difference between normal and {name} was {diff}");
}
};
foreach (var count in Data.Select(x => (int)x[0]))
{
var normal = Normal(count);
var vectorUnaligned = VectorUnaligned(count);
ensureAlmostSame(nameof(vectorUnaligned), normal, vectorUnaligned);
var vectorAligned = VectorAligned(count);
ensureAlmostSame(nameof(vectorAligned), normal, vectorAligned);
var avx2Aligned = Avx2Aligned(count);
ensureAlmostSame(nameof(avx2Aligned), normal, avx2Aligned);
var fmaAligned = FmaAligned(count);
ensureAlmostSame(nameof(fmaAligned), normal, fmaAligned);
}
}
//[Benchmark(Baseline = true)]
[ArgumentsSource(nameof(Data))]
public float Normal(int count)
{
var result = 0f;
for (var i = 0; i < count; i++)
{
result += rawFirstData[i] * rawSecondData[i];
}
return result;
}
[Benchmark]
[ArgumentsSource(nameof(Data))]
public float VectorUnaligned(int count)
{
int vectorSize = Vector<float>.Count;
var accVector = Vector<float>.Zero;
int i = 0;
for (; i <= count - vectorSize; i += vectorSize)
{
var firstVector = new Vector<float>(rawFirstData, i);
var secondVector = new Vector<float>(rawSecondData, i);
var v = Vector.Multiply(firstVector, secondVector);
accVector = Vector.Add(v, accVector);
}
float result = Vector.Sum(accVector);
for (; i < count; i++)
{
result += rawFirstData[i] * rawSecondData[i];
}
return result;
}
//[Benchmark]
[ArgumentsSource(nameof(Data))]
public float VectorAligned(int count)
{
int vectorSize = Vector<float>.Count;
var accVector = Vector<float>.Zero;
int i = 0;
for (; i <= count - vectorSize; i += vectorSize)
{
var firstVector = new Vector<float>(alignedFirstData, alignedFirstDataOffset / sizeof(float) + i);
var secondVector = new Vector<float>(alignedSecondData, alignedSecondDataOffset / sizeof(float) + i);
var v = Vector.Multiply(firstVector, secondVector);
accVector = Vector.Add(v, accVector);
}
float result = Vector.Sum(accVector);
for (; i < count; i++)
{
result += rawFirstData[i] * rawSecondData[i];
}
return result;
}
[Benchmark]
[ArgumentsSource(nameof(Data))]
public float Avx2Aligned(int count)
{
int vectorSize = Vector256<float>.Count;
var accumulationVector = Vector256<float>.Zero;
var i = 0;
for (;i <= count - vectorSize; i += vectorSize)
{
var firstVector = Avx2.LoadAlignedVector256(alignedFirstDataPointer + i);
var secondVector = Avx2.LoadAlignedVector256(alignedSecondDataPointer + i);
var resultVector = Avx2.Multiply(firstVector, secondVector);
accumulationVector = Avx2.Add(accumulationVector, resultVector);
}
var result = 0f;
var temp = stackalloc float[vectorSize];
Avx2.Store(temp, accumulationVector);
for (int j = 0; j < vectorSize; j++)
{
result += temp[j];
}
for (; i < count; i++)
{
result += *(alignedFirstDataPointer + i) * *(alignedSecondDataPointer + i);
}
return result;
}
[Benchmark]
[ArgumentsSource(nameof(Data))]
public float FmaAligned(int count)
{
int vectorSize = Vector256<float>.Count;
var accumulationVector = Vector256<float>.Zero;
var i = 0;
for (; i <= count - vectorSize; i += vectorSize)
{
var firstVector = Avx2.LoadAlignedVector256(alignedFirstDataPointer + i);
var secondVector = Avx2.LoadAlignedVector256(alignedSecondDataPointer + i);
accumulationVector = Fma.MultiplyAdd(firstVector, secondVector, accumulationVector);
}
var result = 0f;
var temp = stackalloc float[vectorSize];
Avx2.Store(temp, accumulationVector);
for (int j = 0; j < vectorSize; j++)
{
result += temp[j];
}
for (; i < count; i++)
{
result += *(alignedFirstDataPointer + i) * *(alignedSecondDataPointer + i);
}
return result;
}
}
If I run this benchmark on my Zen3 CPU, I get the following result:
BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19042.1586 (20H2/October2020Update)
AMD Ryzen 5 5600X, 1 CPU, 12 logical and 6 physical cores
.NET SDK=6.0.200
[Host] : .NET 6.0.2 (6.0.222.6406), X64 RyuJIT
DefaultJob : .NET 6.0.2 (6.0.222.6406), X64 RyuJIT
| Method | count | Mean | Error | StdDev |
|---------------- |------ |---------:|----------:|----------:|
| VectorUnaligned | 8 | 1.231 ns | 0.0093 ns | 0.0082 ns |
| Avx2Aligned | 8 | 3.576 ns | 0.0208 ns | 0.0195 ns |
| FmaAligned | 8 | 3.408 ns | 0.0259 ns | 0.0243 ns |
| VectorUnaligned | 20 | 4.428 ns | 0.0146 ns | 0.0122 ns |
| Avx2Aligned | 20 | 6.321 ns | 0.0578 ns | 0.0541 ns |
| FmaAligned | 20 | 5.845 ns | 0.0121 ns | 0.0113 ns |
| VectorUnaligned | 32 | 4.022 ns | 0.0098 ns | 0.0087 ns |
| Avx2Aligned | 32 | 5.205 ns | 0.0161 ns | 0.0150 ns |
| FmaAligned | 32 | 4.776 ns | 0.0265 ns | 0.0221 ns |
| VectorUnaligned | 50 | 6.901 ns | 0.0337 ns | 0.0315 ns |
| Avx2Aligned | 50 | 7.207 ns | 0.0476 ns | 0.0422 ns |
| FmaAligned | 50 | 7.246 ns | 0.0169 ns | 0.0158 ns |
Why is VectorUnaligned so much faster that the more optimized AVX2 and Fma code?
If I enable VectorAligned its also slower than VectorUnaligned.
Not an answer but a tip for "Fastest way to multiply".
Sorry, I don't know how to deal with alignment but you missed the option of casting the array type. It might be faster than picking floats from source arrays in the loop.
int vectorSize = Vector<float>.Count;
var accVector = Vector<float>.Zero;
Span<Vector<float>> firstVectors = MemoryMarshal.Cast<float, Vector<float>>(rawFirstData);
Span<Vector<float>> secondVectors = MemoryMarshal.Cast<float, Vector<float>>(rawSecondData);
for (int i = 0; i < firstVectors.Length; i++)
{
accVector += Vector.Multiply(firstVectors[i], secondVectors[i]);
}
float result = Vector.Sum(accVector);
for (int i = firstVectors.Length * vectorSize; i < count; i++)
{
result += rawFirstData[i] * rawSecondData[i];
}
It makes a bit more JIT Assembler code than VectorUnaligned method but the first loop looks like twice shorter because if contains only one out-of-range check instead of 4. Give it a chance to test with different types of vectors and alignment.
this one
L0080: movsxd rsi, r11d
L0083: shl rsi, 5
L0087: vmovupd ymm1, [r8+rsi]
L008d: cmp r11d, r9d
L0090: jae short L00ff ; throw out-of-range
L0092: vmovupd ymm2, [r10+rsi]
L0098: vmulps ymm1, ymm1, ymm2
L009c: vaddps ymm0, ymm0, ymm1
L00a0: inc r11d
L00a3: cmp r11d, edx
L00a6: jl short L0080
VectorUnaligned loop, looks like JIT failed to optimize it
L0020: mov r8, rdx
L0023: cmp eax, [r8+8]
L0027: jae L00c3 ; throw out-of-range
L002d: lea r9d, [rax+7]
L0031: cmp r9d, [r8+8]
L0035: jae L00c3 ; throw out-of-range
L003b: vmovupd ymm1, [r8+rax*4+0x10]
L0042: mov r8, [rcx+0x10]
L0046: cmp eax, [r8+8]
L004a: jae L00c3 ; throw out-of-range
L0050: cmp r9d, [r8+8]
L0054: jae short L00c3 ; throw out-of-range
L0056: vmovupd ymm2, [r8+rax*4+0x10]
L005d: vmulps ymm1, ymm1, ymm2
L0061: vaddps ymm0, ymm1, ymm0
L0065: add eax, 8
L0068: mov r8d, [rdx+8]
L006c: sub r8d, 8
L0070: cmp r8d, eax
L0073: jge short L0020
Compiled code got from https://sharplab.io/. Real generated code may vary from CPU to CPU because Vector<T>.Count on certain CPUs may vary.
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);
}
}
}
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
//**$$================================$$
|| <<>><<>> ||
|| <<>>........<<>> ||
|| <<>>................<<>> ||
||<<>>........................<<>>||
||<<>>........................<<>>||
|| <<>>................<<>> ||
|| <<>>........<<>> ||
|| <<>><<>> ||
$$================================$$
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");
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: