Related
How can I make the for loop of this function to use the GPU with OpenCL?
public static double[] Calculate(double[] num, int period)
{
var final = new double[num.Length];
double sum = num[0];
double coeff = 2.0 / (1.0 + period);
for (int i = 0; i < num.Length; i++)
{
sum += coeff * (num[i] - sum);
final[i] = sum;
}
return final;
}
Your problem as written does not fit well with something that would work on a GPU. You cannot parallelize (in a way that improves performance) the operation on a single array because the value of the nth element depends on elements 1 to n. However, you can utilize the GPU to process multiple arrays, where each GPU core operates on a separate array.
The full code for the solution is at the end of the answer, but the results of the test, to calculate on 10,000 arrays each of which has 10,000 elements, generates the following (on a GTX1080M and an i7 7700k with 32GB RAM):
Task Generating Data: 1096.4583ms
Task CPU Single Thread: 596.2624ms
Task CPU Parallel: 179.1717ms
GPU CPU->GPU: 89ms
GPU Execute: 86ms
GPU GPU->CPU: 29ms
Task Running GPU: 921.4781ms
Finished
In this test, we measure the speed at which we can generate results into a managed C# array using the CPU with one thread, the CPU with all threads, and finally the GPU using all cores. We validate that the results from each test are identical, using the function AreTheSame.
The fastest time is processing the arrays on the CPU using all threads (Task CPU Parallel: 179ms).
The GPU is actually the slowest (Task Running GPU: 922ms), but this is because of the time taken to reformat the C# arrays in a way that they can be transferred onto the GPU.
If this bottleneck were removed (which is quite possible, depending on your use case), the GPU could potentially be the fastest. If the data were already formatted in a manner that can be immediately be transferred onto the GPU, the total processing time for the GPU would be 204ms (CPU->GPU: 89ms + Execute: 86ms + GPU->CPU: 29 ms = 204ms). This is still slower than the parallel CPU option, but on a different sort of data set, it might be faster.
To get the data back from the GPU (the most important part of actually using the GPU), we use the function ComputeCommandQueue.Read. This transfers the altered array on the GPU back to the CPU.
To run the following code, reference the Cloo Nuget Package (I used 0.9.1). And make sure to compile on x64 (you will need the memory). You may need to update your graphics card driver too if it fails to find an OpenCL device.
class Program
{
static string CalculateKernel
{
get
{
return #"
kernel void Calc(global int* offsets, global int* lengths, global double* doubles, double periodFactor)
{
int id = get_global_id(0);
int start = offsets[id];
int length = lengths[id];
int end = start + length;
double sum = doubles[start];
for(int i = start; i < end; i++)
{
sum = sum + periodFactor * ( doubles[i] - sum );
doubles[i] = sum;
}
}";
}
}
public static double[] Calculate(double[] num, int period)
{
var final = new double[num.Length];
double sum = num[0];
double coeff = 2.0 / (1.0 + period);
for (int i = 0; i < num.Length; i++)
{
sum += coeff * (num[i] - sum);
final[i] = sum;
}
return final;
}
static void Main(string[] args)
{
int maxElements = 10000;
int numArrays = 10000;
int computeCores = 2048;
double[][] sets = new double[numArrays][];
using (Timer("Generating Data"))
{
Random elementRand = new Random(1);
for (int i = 0; i < numArrays; i++)
{
sets[i] = GetRandomDoubles(elementRand.Next((int)(maxElements * 0.9), maxElements), randomSeed: i);
}
}
int period = 14;
double[][] singleResults;
using (Timer("CPU Single Thread"))
{
singleResults = CalculateCPU(sets, period);
}
double[][] parallelResults;
using (Timer("CPU Parallel"))
{
parallelResults = CalculateCPUParallel(sets, period);
}
if (!AreTheSame(singleResults, parallelResults)) throw new Exception();
double[][] gpuResults;
using (Timer("Running GPU"))
{
gpuResults = CalculateGPU(computeCores, sets, period);
}
if (!AreTheSame(singleResults, gpuResults)) throw new Exception();
Console.WriteLine("Finished");
Console.ReadKey();
}
public static bool AreTheSame(double[][] a1, double[][] a2)
{
if (a1.Length != a2.Length) return false;
for (int i = 0; i < a1.Length; i++)
{
var ar1 = a1[i];
var ar2 = a2[i];
if (ar1.Length != ar2.Length) return false;
for (int j = 0; j < ar1.Length; j++)
if (Math.Abs(ar1[j] - ar2[j]) > 0.0000001) return false;
}
return true;
}
public static double[][] CalculateGPU(int partitionSize, double[][] sets, int period)
{
ComputeContextPropertyList cpl = new ComputeContextPropertyList(ComputePlatform.Platforms[0]);
ComputeContext context = new ComputeContext(ComputeDeviceTypes.Gpu, cpl, null, IntPtr.Zero);
ComputeProgram program = new ComputeProgram(context, new string[] { CalculateKernel });
program.Build(null, null, null, IntPtr.Zero);
ComputeCommandQueue commands = new ComputeCommandQueue(context, context.Devices[0], ComputeCommandQueueFlags.None);
ComputeEventList events = new ComputeEventList();
ComputeKernel kernel = program.CreateKernel("Calc");
double[][] results = new double[sets.Length][];
double periodFactor = 2d / (1d + period);
Stopwatch sendStopWatch = new Stopwatch();
Stopwatch executeStopWatch = new Stopwatch();
Stopwatch recieveStopWatch = new Stopwatch();
int offset = 0;
while (true)
{
int first = offset;
int last = Math.Min(offset + partitionSize, sets.Length);
int length = last - first;
var merged = Merge(sets, first, length);
sendStopWatch.Start();
ComputeBuffer<int> offsetBuffer = new ComputeBuffer<int>(
context,
ComputeMemoryFlags.ReadWrite | ComputeMemoryFlags.UseHostPointer,
merged.Offsets);
ComputeBuffer<int> lengthsBuffer = new ComputeBuffer<int>(
context,
ComputeMemoryFlags.ReadWrite | ComputeMemoryFlags.UseHostPointer,
merged.Lengths);
ComputeBuffer<double> doublesBuffer = new ComputeBuffer<double>(
context,
ComputeMemoryFlags.ReadWrite | ComputeMemoryFlags.UseHostPointer,
merged.Doubles);
kernel.SetMemoryArgument(0, offsetBuffer);
kernel.SetMemoryArgument(1, lengthsBuffer);
kernel.SetMemoryArgument(2, doublesBuffer);
kernel.SetValueArgument(3, periodFactor);
sendStopWatch.Stop();
executeStopWatch.Start();
commands.Execute(kernel, null, new long[] { merged.Lengths.Length }, null, events);
executeStopWatch.Stop();
using (var pin = Pinned(merged.Doubles))
{
recieveStopWatch.Start();
commands.Read(doublesBuffer, false, 0, merged.Doubles.Length, pin.Address, events);
commands.Finish();
recieveStopWatch.Stop();
}
for (int i = 0; i < merged.Lengths.Length; i++)
{
int len = merged.Lengths[i];
int off = merged.Offsets[i];
var res = new double[len];
Array.Copy(merged.Doubles,off,res,0,len);
results[first + i] = res;
}
offset += partitionSize;
if (offset >= sets.Length) break;
}
Console.WriteLine("GPU CPU->GPU: " + recieveStopWatch.ElapsedMilliseconds + "ms");
Console.WriteLine("GPU Execute: " + executeStopWatch.ElapsedMilliseconds + "ms");
Console.WriteLine("GPU GPU->CPU: " + sendStopWatch.ElapsedMilliseconds + "ms");
return results;
}
public static PinnedHandle Pinned(object obj) => new PinnedHandle(obj);
public class PinnedHandle : IDisposable
{
public IntPtr Address => handle.AddrOfPinnedObject();
private GCHandle handle;
public PinnedHandle(object val)
{
handle = GCHandle.Alloc(val, GCHandleType.Pinned);
}
public void Dispose()
{
handle.Free();
}
}
public class MergedResults
{
public double[] Doubles { get; set; }
public int[] Lengths { get; set; }
public int[] Offsets { get; set; }
}
public static MergedResults Merge(double[][] sets, int offset, int length)
{
List<int> lengths = new List<int>(length);
List<int> offsets = new List<int>(length);
for (int i = 0; i < length; i++)
{
var arr = sets[i + offset];
lengths.Add(arr.Length);
}
var totalLength = lengths.Sum();
double[] doubles = new double[totalLength];
int dataOffset = 0;
for (int i = 0; i < length; i++)
{
var arr = sets[i + offset];
Array.Copy(arr, 0, doubles, dataOffset, arr.Length);
offsets.Add(dataOffset);
dataOffset += arr.Length;
}
return new MergedResults()
{
Doubles = doubles,
Lengths = lengths.ToArray(),
Offsets = offsets.ToArray(),
};
}
public static IDisposable Timer(string name)
{
return new SWTimer(name);
}
public class SWTimer : IDisposable
{
private Stopwatch _sw;
private string _name;
public SWTimer(string name)
{
_name = name;
_sw = Stopwatch.StartNew();
}
public void Dispose()
{
_sw.Stop();
Console.WriteLine("Task " + _name + ": " + _sw.Elapsed.TotalMilliseconds + "ms");
}
}
public static double[][] CalculateCPU(double[][] arrays, int period)
{
double[][] results = new double[arrays.Length][];
for (var index = 0; index < arrays.Length; index++)
{
var arr = arrays[index];
results[index] = Calculate(arr, period);
}
return results;
}
public static double[][] CalculateCPUParallel(double[][] arrays, int period)
{
double[][] results = new double[arrays.Length][];
Parallel.For(0, arrays.Length, i =>
{
var arr = arrays[i];
results[i] = Calculate(arr, period);
});
return results;
}
static double[] GetRandomDoubles(int num, int randomSeed)
{
Random r = new Random(randomSeed);
var res = new double[num];
for (int i = 0; i < num; i++)
res[i] = r.NextDouble() * 0.9 + 0.05;
return res;
}
}
as commenter Cory stated refer to this link for setup.
How to use your GPU in .NET
Here is how you would use this project:
Add the Nuget Package Cloo
Add reference to OpenCLlib.dll
Download OpenCLLib.zip
Add using OpenCL
static void Main(string[] args)
{
int[] Primes = { 1,2,3,4,5,6,7 };
EasyCL cl = new EasyCL();
cl.Accelerator = AcceleratorDevice.GPU;
cl.LoadKernel(IsPrime);
cl.Invoke("GetIfPrime", 0, Primes.Length, Primes, 1.0);
}
static string IsPrime
{
get
{
return #"
kernel void GetIfPrime(global int* num, int period)
{
int index = get_global_id(0);
int sum = (2.0 / (1.0 + period)) * (num[index] - num[0]);
printf("" %d \n"",sum);
}";
}
}
for (int i = 0; i < num.Length; i++)
{
sum += coeff * (num[i] - sum);
final[i] = sum;
}
means first element is multiplied by coeff 1 time and subtracted from 2nd element. First element also multiplied by square of coeff and this time added to 3rd element. Then first element multiplied by cube of coeff and subtracted from 4th element.
This is going like this:
-e0*c*c*c + e1*c*c - e2*c = f3
e0*c*c*c*c - e1*c*c*c + e2*c*c - e3*c = f4
-e0*c*c*c*c*c + e1*c*c*c*c - e2*c*c*c + e3*c*c - e4*c =f5
For all elements, scan through for all smaller id elements and compute this:
if difference of id values(lets call it k) of elements is odd, take subtraction, if not then take addition. Before addition or subtraction, multiply that value by k-th power of coeff. Lastly, multiply the current num value by coefficient and add it to current cell. Current cell value is final(i).
This is O(N*N) and looks like an all-pairs compute kernel. An example using an open-source C# OpenCL project:
ClNumberCruncher cruncher = new ClNumberCruncher(ClPlatforms.all().gpus(), #"
__kernel void foo(__global double * num, __global double * final, __global int *parameters)
{
int threadId = get_global_id(0);
int period = parameters[0];
double coeff = 2.0 / (1.0 + period);
double sumOfElements = 0.0;
for(int i=0;i<threadId;i++)
{
// negativity of coeff is to select addition or subtraction for different powers of coeff
double powKofCoeff = pow(-coeff,threadId-i);
sumOfElements += powKofCoeff * num[i];
}
final[threadId] = sumOfElements + num[threadId] * coeff;
}
");
cruncher.performanceFeed = true; // getting benchmark feedback on console
double[] numArray = new double[10000];
double[] finalArray = new double[10000];
int[] parameters = new int[10];
int period = 15;
parameters[0] = period;
ClArray<double> numGpuArray = numArray;
numGpuArray.readOnly = true; // gpus read this from host
ClArray<double> finalGpuArray = finalArray; // finalArray will have results
finalGpuArray.writeOnly = true; // gpus write this to host
ClArray<int> parametersGpu = parameters;
parametersGpu.readOnly = true;
// calculate kernels with exact same ordering of parameters
// num(double),final(double),parameters(int)
// finalGpuArray points to __global double * final
numGpuArray.nextParam(finalGpuArray, parametersGpu).compute(cruncher, 1, "foo", 10000, 100);
// first compute always lags because of compiling the kernel so here are repeated computes to get actual performance
numGpuArray.nextParam(finalGpuArray, parametersGpu).compute(cruncher, 1, "foo", 10000, 100);
numGpuArray.nextParam(finalGpuArray, parametersGpu).compute(cruncher, 1, "foo", 10000, 100);
Results are on finalArray array for 10000 elements, using 100 workitems per workitem-group.
GPGPU part takes 82ms on a rx550 gpu which has very low ratio of 64bit-to-32bit compute performance(because consumer gaming cards are not good at double precision for new series). An Nvidia Tesla or an Amd Vega would easily compute this kernel without crippled performance. Fx8150(8 cores) completes in 683ms. If you need to specifically select only an integrated-GPU and its CPU, you can use
ClPlatforms.all().gpus().devicesWithHostMemorySharing() + ClPlatforms.all().cpus() when creating ClNumberCruncher instance.
binaries of api:
https://www.codeproject.com/Articles/1181213/Easy-OpenCL-Multiple-Device-Load-Balancing-and-Pip
or source code to compile on your pc:
https://github.com/tugrul512bit/Cekirdekler
if you have multiple gpus, it uses them without any extra code. Including a cpu to the computations would pull gpu effectiveness down in this sample for first iteration (repeatations complete in 76ms with cpu+gpu) so its better to use 2-3 GPU instead of CPU+GPU.
I didn't check numerical stability(you should use Kahan-Summation when adding millions or more values into same variable but I didn't use it for readability and don't have an idea about if 64-bit values need this too like 32-bit ones) or any value correctness, you should do it. Also foo kernel is not optimized. It makes %50 of core times idle so it should be better scheduled like this:
thread-0: compute element 0 and element N-1
thread-1: compute element 1 and element N-2
thread-m: compute element N/2-1 and element N/2
so all workitems get similar amount of work. On top of this, using 100 for workgroup size is not optimal. It should be something like 128,256,512 or 1024(for Nvidia) but this means array size should also be an integer multiple of this too. Then it would need extra control logic in the kernel to not go out of array borders. For even more performance, for loop could have multiple partial sums to do a "loop unrolling".
I am looking for a C# algorithm that would give me a set of random integers from input List, such that the sum of obtained random integers is N.
For example:
If the list is {1,2,3,4,5,6...100} and N is 20, then the algorithm should return a set of random numbers like {5,6,9} or {9,11} or {1,2,3,4,10} etc.
Note that the count of integers in result set need not be fixed. Also, the input list can have duplicate integers. Performance is one of my priority as the input list can be large (around 1000 integers) and I need to randomize about 2-3 times in a single web request. I am flexible with not sticking to List as datatype if there is a performance issue with Lists.
I have tried below method which is very rudimentary and performance inefficient:
Use the Random class to get a random index from the input list
Get the integer from input list present at index obtained in #1. Lets call this integer X.
Sum = Sum + X.
Remove X from input list so that it does not get selected next.
If Sum is less than required total N, add X to outputList and go back to #1.
If the Sum is more than required total N, reinitialize everything and restart the process.
If the Sum is equal to required total N, return outputList
while(!reachedTotal)
{
//Initialize everything
inputList.AddRange(originalInputList);
outputList = new List<int>();
while (!reachedTotal)
{
random = r.Next(inputList.Count);
sum += inputList.ElementAt(random);
if(sum<N)
{
outputList.Add(inputList.ElementAt(random));
inputList.RemoveAt(random);
}
else if(sum>N)
break;
else
reachedTotal = true;
}
}
This is a stochastical approach that gives you a solution within a 10% range of N - Assuming one exists
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace StackOverflowSnippets
{
class Program
{
static void Main(string[] args)
{
// ----------------------------------------------------------------------------------
// The code you are interested in starts below this line
const Int32 N = 100;
Int32 nLowerBound = (90 * N) / 100; Int32 nUpperBound = (110 * N) / 100;
Random rnd = new Random();
Int32 runningSum = 0;
Int32 nextIndex = 0;
List<Int32> inputList = GenerateRandomList( /* entries = */ 1000);
List<Int32> o = new List<Int32>();
while (runningSum < nLowerBound)
{
nextIndex = rnd.Next(inputList.Count); if (nUpperBound < (runningSum + inputList[nextIndex])) continue;
runningSum += inputList[nextIndex];
o.Add(inputList[nextIndex]);
inputList.RemoveAt(nextIndex);
}
// The code you are interested in ends above this line
// ----------------------------------------------------------------------------------
StringBuilder b = new StringBuilder();
for(Int32 i = 0; i < o.Count;i++)
{
if (b.Length != 0) b.Append(",");
b.Append(o[i].ToString());
}
Console.WriteLine("Exact N : " + N);
Console.WriteLine("Upper Bound: " + nUpperBound);
Console.WriteLine("Lower Bound: " + nLowerBound);
Console.WriteLine();
Console.WriteLine("sum(" + b.ToString() + ")=" + GetSum(o).ToString());
Console.ReadLine();
}
// -------------------------------------------------------------------
#region Helper methods
private static object GetSum(List<int> o)
{
Int32 sum = 0;
foreach (Int32 i in o) sum += i;
return sum;
}
private static List<Int32> GenerateRandomList(Int32 entries)
{
List<Int32> l = new List<Int32>();
for(Int32 i = 1; i < entries; i++)
{
l.Add(i);
}
return l;
}
#endregion
}
}
EDIT
Forgot to remove the element from the input-list so it cannot be selected twice
Fixed the 'remove element' insertion
I'm currently creating a simple single layer perceptron algorithm. It should take in a .txt file and the algorithm runs over it. At the moment, I have the algorithm and just hard coded sample data values to test if it works (which it does), but I need it to feed off existing data values from a file. I have tried to make it work, but due to my inexperience in coding, I haven't succeeded. It would be awesome if someone could help me get this code working with external data as I've been pulling my hair out. The data is formatted like below (obviously without the bullet numbers).
0.651769
0.651604
0.651609
0.651679
0.651667
0.651699
Current Code:
using System;
using System.IO;
using System.Collections.Generic;
namespace Network
{
public class Network
{
static void Main()
{
// Load sample input patterns.
double[,] inputs = new double[,] {
{0.99, 0.99}, {0.99, 0.99}, {0.99, 0.99}, {0.99, 095}};
// Load sample output patterns.
int[] outputs = new int[] {0, 1, 0, 0 };
int patternCount = inputs.GetUpperBound(0) + 1;
// Randomise weights.
Random rdm = new Random();
// Setting randomly generated weights between -0.5 and +0.5
double[] weights = {rdm.NextDouble()-0.5, rdm.NextDouble()-0.5, rdm.NextDouble()};
// Set learning rate.
double learningRate = 1;
// Start iteration at 0
int iteration = 1;
double ErrorRate;
do
{
// Global error set to 0
ErrorRate = 0;
for (int j = 0; j < patternCount; j++)
{
// Calculate output.
int output = Output(weights, inputs[j, 0], inputs[j, 1]);
// Calculate error.
double localError = outputs[j] - output;
//if the localError is not equal to zero
if (localError != 0)
{
// Update weights.
for (int i = 0; i < 2; i++)
{
weights[i] += learningRate * localError * inputs[j, i] / 2;
}
}
// Convert error to absolute value.
ErrorRate += (localError);
}
Console.WriteLine("Iteration {0}\tError {1}", iteration, ErrorRate);
iteration++;
// If the Error is equal to zero then calculate
} while (ErrorRate != 0);
// Convergence
Console.WriteLine();
Console.WriteLine("[Input1] [Input2] [Output]");
// Input1 values
for (double input1 = 0; input1 <= 1; input1 += 1)
{
// Input2 values
for (double input2 = 0; input2 <= 1; input2 += 1)
{
// Calculate output with the inputs and the randomly generated weights
int output = Output(weights, input1, input2);
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine(" {0} {1} {2}", input1, input2, (output == 1) ? "1" : "0");
}
}
Console.Read();
}
private static int Output(double[] weights, double input1, double input2)
{
// Output = input1 * weight1 + input2 * weight2 + bias * weight3
double sum = input1 * weights[0] + input2 * weights[1] + 1 * weights[2];
// If the first condition is true, then it becomes the result. If not, the second condition becomes the result
return (sum >= 0) ? 1 : 0;
}
}
}
Are you looking for
using System.Globalization;
using System.IO;
using System.Linq
...
double[] data = File
.ReadLines(#"C:\MyFile.txt") //TODO: put actual file here
.Select(line => Double.Parse(line, CultureInfo.InvariantCulture))
.ToArray();
I'm studying coding and I'm on project 8 of project euler.
I was able to show the product "5832" for four adjacent digits when I'm using my code however when I use it on 13 digits, it doesn't work. My code is:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace practice
{
class Program
{
static void Main(string[] args)
{
const string number = "7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450";
string input1, input2, input3, input4, input5;
string input6, input7, input8, input9, input10;
string input11, input12, input13;
int convert1, convert2, convert3, convert4, convert5;
int convert6, convert7, convert8, convert9, convert10;
int convert11, convert12, convert13;
convert1 = convert2 = convert3 = convert4 = convert5 = convert6 = convert7 = convert8 = 0;
convert9 = convert10 = convert11 = convert12 = convert13 = 0;
int counter;
int product = 0;
int largest = 0;
int length = number.Length - 13;
for (counter = 1; counter <= length; counter++)
{
input1 = number.Substring(counter, 1);
input2 = number.Substring(counter+1, 1);
input3 = number.Substring(counter+2, 1);
input4 = number.Substring(counter+3, 1);
input5 = number.Substring(counter+4, 1);
input6 = number.Substring(counter+5, 1);
input7 = number.Substring(counter+6, 1);
input8 = number.Substring(counter+7, 1);
input9 = number.Substring(counter+8, 1);
input10 = number.Substring(counter+9, 1);
input11 = number.Substring(counter+10, 1);
input12 = number.Substring(counter+11, 1);
input13 = number.Substring(counter+12, 1);
convert1 = Convert.ToInt32(input1);
convert2 = Convert.ToInt32(input2);
convert3 = Convert.ToInt32(input3);
convert4 = Convert.ToInt32(input4);
convert5 = Convert.ToInt32(input5);
convert6 = Convert.ToInt32(input6);
convert7 = Convert.ToInt32(input7);
convert8 = Convert.ToInt32(input8);
convert9 = Convert.ToInt32(input9);
convert10 = Convert.ToInt32(input10);
convert11 = Convert.ToInt32(input11);
convert12 = Convert.ToInt32(input12);
convert13 = Convert.ToInt32(input13);
product = convert1 * convert2 * convert3 * convert4 * convert5 * convert6
* convert7 * convert8 * convert9 * convert10 * convert11
* convert12 * convert13;
if (largest < product) { largest = product; }
}
Console.WriteLine("The largest number is {0}", largest);
Console.ReadKey();
}
}
}
It doesn't show the correct answer which I find daunting. The next steps
I did is:
1. Check the last 13 digits of my variables to check if it loops and multiplies correctly "0420420752963450".
2. Check if it works with the first four numbers and first five numbers which are surprisingly correct.
3. Studied how others have done it.
4. Links used:
Homework in Java. Find the largest product of five consecutive digits
http://www.mathblog.dk/solution-to-problem-8-of-project-euler/
I seem to not get it. Please guide me on seeing my mistake. Thank you.
Your product variable is int and you parse your consecutive numbers to int. Max value for integer is 2,147,483,647. So when you multiply 13 numbers it's quite possible you will exceed the limit and the value will overflow giving incorrect result. Perhaps, consider using BigInteger instead.
What about something like this:
[Test]
public void Test()
{
const string number = "7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450";
int index = 0;
int largest = 0;
var largestString = "";
const int length = 13;
while (index + length <= number.Length)
{
var substring = number.Substring(index, length);
var product = ProductOfEachNumber(substring);
if (product > largest)
{
largestString = substring;
largest = product;
}
index++;
}
Console.WriteLine("The largest number is {0}", largest);
Console.WriteLine("The largest number string is {0}", largestString);
}
[Test]
public void TestSubstring()
{
var res = ProductOfEachNumber("9989");
res.Should().Be(5832);
}
private static int ProductOfEachNumber(string substring)
{
return substring.Aggregate(1, (current, c) => current*Int32.Parse(c.ToString()));
}
Gives output:
The largest number is 2091059712
The largest number string is 9781797784617
Try this one, let me know. You have to modify it.
static void Main(string[] args)
{
Stopwatch sw = Stopwatch.StartNew();
int x = Largest();
Console.WriteLine(x);
Console.WriteLine("Time used (float): {0} ms", sw.Elapsed.TotalMilliseconds);
Console.WriteLine("Time used (rounded): {0} ms", sw.ElapsedMilliseconds);
Console.ReadKey();
}
public static int Largest()
{
string p = "7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450";
int largest = 0;
int numm = 0;
for (int i = 0; i < p.Length - 4; i++)
{
numm = int.Parse(p.Substring(i, 1)) *
int.Parse(p.Substring(i + 1, 1)) *
int.Parse(p.Substring(i + 2, 1)) *
int.Parse(p.Substring(i + 3, 1)) *
int.Parse(p.Substring(i + 4, 1));
if (numm > largest)
{
largest = numm;
}
}
return largest;
}
How can I change my C# code below to list all possible permutations without repetitions? For example: The result of 2 dice rolls would produce 1,1,2 so that means 2,1,1 should not appear.
Below is my code:
string[] Permutate(int input)
{
string[] dice;
int numberOfDice = input;
const int diceFace = 6;
dice = new string[(int)Math.Pow(diceFace, numberOfDice)];
int indexNumber = (int)Math.Pow(diceFace, numberOfDice);
int range = (int)Math.Pow(diceFace, numberOfDice) / 6;
int diceNumber = 1;
int counter = 0;
for (int i = 1; i <= indexNumber; i++)
{
if (range != 0)
{
dice[i - 1] += diceNumber + " ";
counter++;
if (counter == range)
{
counter = 0;
diceNumber++;
}
if (i == indexNumber)
{
range /= 6;
i = 0;
}
if (diceNumber == 7)
{
diceNumber = 1;
}
}
Thread.Sleep(1);
}
return dice;
}
The simplest possible way I could think of:
List<string> dices = new List<string>();
for (int i = 1; i <= 6; i++)
{
for (int j = i; j <= 6; j++)
{
for (int k = j; k <= 6; k++)
{
dices.Add(string.Format("{0} {1} {2}", i, j, k));
}
}
}
I have written a class to handle common functions for working with the binomial coefficient, which is the type of problem that your problem falls under. It performs the following tasks:
Outputs all the K-indexes in a nice format for any N choose K to a file. The K-indexes can be substituted with more descriptive strings or letters. This method makes solving this type of problem quite trivial.
Converts the K-indexes to the proper index of an entry in the sorted binomial coefficient table. This technique is much faster than older published techniques that rely on iteration. It does this by using a mathematical property inherent in Pascal's Triangle. My paper talks about this. I believe I am the first to discover and publish this technique, but I could be wrong.
Converts the index in a sorted binomial coefficient table to the corresponding K-indexes.
Uses Mark Dominus method to calculate the binomial coefficient, which is much less likely to overflow and works with larger numbers.
The class is written in .NET C# and provides a way to manage the objects related to the problem (if any) by using a generic list. The constructor of this class takes a bool value called InitTable that when true will create a generic list to hold the objects to be managed. If this value is false, then it will not create the table. The table does not need to be created in order to perform the 4 above methods. Accessor methods are provided to access the table.
There is an associated test class which shows how to use the class and its methods. It has been extensively tested with 2 cases and there are no known bugs.
To read about this class and download the code, see Tablizing The Binomial Coeffieicent.
I'm bad at math as well, this may or may not be helpful...
Program.cs
namespace Permutation
{
using System;
using System.Collections.Generic;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Generating list.");
var dice = new List<ThreeDice>();
for (int x = 1; x <= 6; x++)
{
for (int y = 1; y <= 6; y++)
{
for (int z = 1; z <= 6; z++)
{
var die = new ThreeDice(x, y, z);
if (dice.Contains(die))
{
Console.WriteLine(die + " already exists.");
}
else
{
dice.Add(die);
}
}
}
}
Console.WriteLine(dice.Count + " permutations generated.");
foreach (var die in dice)
{
Console.WriteLine(die);
}
Console.ReadKey();
}
}
}
ThreeDice.cs
namespace Permutation
{
using System;
using System.Collections.Generic;
public class ThreeDice : IEquatable<ThreeDice>
{
public ThreeDice(int dice1, int dice2, int dice3)
{
this.Dice = new int[3];
this.Dice[0] = dice1;
this.Dice[1] = dice2;
this.Dice[2] = dice3;
}
public int[] Dice { get; private set; }
// IEquatable implements this method. List.Contains() will use this method to see if there's a match.
public bool Equals(ThreeDice other)
{
// Get the current dice values into a list.
var currentDice = new List<int>(this.Dice);
// Check to see if the same values exist by removing them one by one.
foreach (int die in other.Dice)
{
currentDice.Remove(die);
}
// If the list is empty, we have a match.
return currentDice.Count == 0;
}
public override string ToString()
{
return "<" + this.Dice[0] + "," + this.Dice[1] + "," + this.Dice[2] + ">";
}
}
}
Good luck.
The important part of the question is that you want distinct sets (regardless of order). So for example, a dice roll of [1, 2, 1] is equal to a dice roll of [1, 1, 2].
I'm sure there are a number of ways to skin this cat, but the first thought that comes to mind is to create a EqualityComparer which will compare the list of dice in the way you want, and then use LINQ with the Distinct() method.
Here is the EqualityComparer, which takes 2 List<int> and says they are equal if the elements are all equal (regardless of order):
private class ListComparer : EqualityComparer<List<int>>
{
public override bool Equals(List<int> x, List<int> y)
{
if (x.Count != y.Count)
return false;
x.Sort();
y.Sort();
for (int i = 0; i < x.Count; i++)
{
if (x[i] != y[i])
return false;
}
return true;
}
public override int GetHashCode(List<int> list)
{
int hc = 0;
foreach (var i in list)
hc ^= i;
return hc;
}
}
And here is the code that uses it. I'm using LINQ to build up the list of all combinations... you could also do this with nested for loops but I like this better for some reason:
public static void Main()
{
var values = new[] { 1,2,3,4,5,6 };
var allCombos = from x in values
from y in values
from z in values
select new List<int>{ x, y, z };
var distinctCombos = allCombos.Distinct(new ListComparer());
Console.WriteLine("#All combos: {0}", allCombos.Count());
Console.WriteLine("#Distinct combos: {0}", distinctCombos.Count());
foreach (var combo in distinctCombos)
Console.WriteLine("{0},{1},{2}", combo[0], combo[1], combo[2]);
}
Hope that helps!
Here is generic c# version using recursion (basically the recursive method takes number of dices or number of times the dice has been tossed) and returns all the combinations strings ( for ex, for '3' as per the question - there will be 56 such combinations).
public string[] GetDiceCombinations(int noOfDicesOrnoOfTossesOfDice)
{
noOfDicesOrnoOfTossesOfDice.Throw("noOfDicesOrnoOfTossesOfDice",
n => n <= 0);
List<string> values = new List<string>();
this.GetDiceCombinations_Recursive(noOfDicesOrnoOfTossesOfDice, 1, "",
values);
return values.ToArray();
}
private void GetDiceCombinations_Recursive(int size, int index, string currentValue,
List<string> values)
{
if (currentValue.Length == size)
{
values.Add(currentValue);
return;
}
for (int i = index; i <= 6; i++)
{
this.GetDiceCombinations_Recursive(size, i, currentValue + i, values);
}
}
Below are corresponding tests...
[TestMethod]
public void Dice_Tests()
{
int[] cOut = new int[] { 6, 21, 56, 126 };
for(int i = 1; i<=4; i++)
{
var c = this.GetDiceCombinations(i);
Assert.AreEqual(cOut[i - 1], c.Length);
}
}