I need to optimise code that counts pos/neg values and remove non-qualified values by time.
I have queue of values with time-stamp attached.
I need to discard values which are 1ms old and count negative and positive values. here is pseudo code
list<val> l;
v = q.dequeue();
deleteold(l, v.time);
l.add(v);
negcount = l.count(i => i.value < 0);
poscount = l.count(i => i.value >= 0);
if(negcount == 10) return -1;
if(poscount == 10) return 1;
I need this code in c# working with max speed. No need to stick to the List. In fact arrays separated for neg and pos values are welcome.
edit: probably unsafe arrays will be the best. any hints?
EDIT: thanks for the heads up.. i quickly tested array version vs list (which i already have) and the list is faster: 35 vs 16 ms for 1 mil iterations...
Here is the code for fairness sake:
class Program
{
static int LEN = 10;
static int LEN1 = 9;
static void Main(string[] args)
{
Var[] data = GenerateData();
Stopwatch sw = new Stopwatch();
for (int i = 0; i < 30; i++)
{
sw.Reset();
ArraysMethod(data, sw);
Console.Write("Array: {0:0.0000}ms ", sw.ElapsedTicks / 10000.0);
sw.Reset();
ListMethod(data, sw);
Console.WriteLine("List: {0:0.0000}ms", sw.ElapsedTicks / 10000.0);
}
Console.ReadLine();
}
private static void ArraysMethod(Var[] data, Stopwatch sw)
{
int signal = 0;
int ni = 0, pi = 0;
Var[] n = new Var[LEN];
Var[] p = new Var[LEN];
for (int i = 0; i < LEN; i++)
{
n[i] = new Var();
p[i] = new Var();
}
sw.Start();
for (int i = 0; i < DATALEN; i++)
{
Var v = data[i];
if (v.val < 0)
{
int x = 0;
ni = 0;
// time is not sequential
for (int j = 0; j < LEN; j++)
{
long diff = v.time - n[j].time;
if (diff < 0)
diff = 0;
// too old
if (diff > 10000)
x = j;
else
ni++;
}
n[x] = v;
if (ni >= LEN1)
signal = -1;
}
else
{
int x = 0;
pi = 0;
// time is not sequential
for (int j = 0; j < LEN; j++)
{
long diff = v.time - p[j].time;
if (diff < 0)
diff = 0;
// too old
if (diff > 10000)
x = j;
else
pi++;
}
p[x] = v;
if (pi >= LEN1)
signal = 1;
}
}
sw.Stop();
}
private static void ListMethod(Var[] data, Stopwatch sw)
{
int signal = 0;
List<Var> d = new List<Var>();
sw.Start();
for (int i = 0; i < DATALEN; i++)
{
Var v = data[i];
d.Add(new Var() { time = v.time, val = v.val < 0 ? -1 : 1 });
// delete expired
for (int j = 0; j < d.Count; j++)
{
if (v.time - d[j].time < 10000)
d.RemoveAt(j--);
else
break;
}
int cnt = 0;
int k = d.Count;
for (int j = 0; j < k; j++)
{
cnt += d[j].val;
}
if ((cnt >= 0 ? cnt : -cnt) >= LEN)
signal = 9;
}
sw.Stop();
}
static int DATALEN = 1000000;
private static Var[] GenerateData()
{
Random r = new Random(DateTime.Now.Millisecond);
Var[] data = new Var[DATALEN];
Var prev = new Var() { val = 0, time = DateTime.Now.TimeOfDay.Ticks};
for (int i = 0; i < DATALEN; i++)
{
int x = r.Next(20);
data[i] = new Var() { val = x - 10, time = prev.time + x * 1000 };
}
return data;
}
class Var
{
public int val;
public long time;
}
}
To get negcount and poscount, you are traversing the entire list twice.
Instead, traverse it once (to compute negcount), and then poscount = l.Count - negcount.
Some ideas:
Only count until max(negcount,poscount) becomes 10, then quit (no need to count the rest). Only works if 10 is the maximum count.
Count negative and positive items in 1 go.
Calculate only negcount and infer poscount from count-negcount which is easier to do than counting them both.
Whether any of them are faster than what you have now, and which is fastest, depends among other things on what the data typically looks like. Is it long? Short?
Some more about 3:
You can use trickery to avoid branches here. You don't have to test whether the item is negative, you can add its negativity to a counter. Supposing the item is x and it is an int, x >> 31 is 0 for positive x and -1 for negative x. So counter -= x >> 31 will give negcount.
Edit: unsafe arrays can be faster, but shouldn't be in this case, because the loop would be of the form
for (int i = 0; i < array.Length; i++)
do something with array[i];
Which is optimized by the JIT compiler.
Related
I am working on a project that compares the time bubble and selection sort take. I made two separate programs and combined them into one and now bubble sort is running much faster than selection sort. I checked to make sure that the code wasn't just giving me 0s because of some conversion error and was running as intended. I am using System.Diagnostics; to measure the time. I also checked that the machine was not the problem, I ran it on Replit and got similar results.
{
class Program
{
public static int s1 = 0;
public static int s2 = 0;
static decimal bubblesort(int[] arr1)
{
int n = arr1.Length;
var sw1 = Stopwatch.StartNew();
for (int i = 0; i < n - 1; i++)
{
for (int j = 0; j < n - i - 1; j++)
{
if (arr1[j] > arr1[j + 1])
{
int tmp = arr1[j];
// swap tmp and arr[i] int tmp = arr[j];
arr1[j] = arr1[j + 1];
arr1[j + 1] = tmp;
s1++;
}
}
}
sw1.Stop();
// Console.WriteLine(sw1.ElapsedMilliseconds);
decimal a = Convert.ToDecimal(sw1.ElapsedMilliseconds);
return a;
}
static decimal selectionsort(int[] arr2)
{
int n = arr2.Length;
var sw1 = Stopwatch.StartNew();
// for (int e = 0; e < 1000; e++)
// {
for (int x = 0; x < arr2.Length - 1; x++)
{
int minPos = x;
for (int y = x + 1; y < arr2.Length; y++)
{
if (arr2[y] < arr2[minPos])
minPos = y;
}
if (x != minPos && minPos < arr2.Length)
{
int temp = arr2[minPos];
arr2[minPos] = arr2[x];
arr2[x] = temp;
s2++;
}
}
// }
sw1.Stop();
// Console.WriteLine(sw1.ElapsedMilliseconds);
decimal a = Convert.ToDecimal(sw1.ElapsedMilliseconds);
return a;
}
static void Main(string[] args)
{
Console.WriteLine("Enter the size of n");
int n = Convert.ToInt32(Console.ReadLine());
Random rnd = new System.Random();
decimal bs = 0M;
decimal ss = 0M;
int s = 0;
int[] arr1 = new int[n];
int tx = 1000; //tx is a variable that I can use to adjust sample size
decimal tm = Convert.ToDecimal(tx);
for (int i = 0; i < tx; i++)
{
for (int a = 0; a < n; a++)
{
arr1[a] = rnd.Next(0, 1000000);
}
ss += selectionsort(arr1);
bs += bubblesort(arr1);
}
bs = bs / tm;
ss = ss / tm;
Console.WriteLine("Bubble Sort took " + bs + " miliseconds");
Console.WriteLine("Selection Sort took " + ss + " miliseconds");
}
}
}
What is going on? What is causing bubble sort to be fast or what is slowing down Selection sort? How can I fix this?
I found that the problem was that the Selection Sort was looping 1000 times per method run in addition to the 1000 runs for sample size, causing the method to perform significantly worse than bubble sort. Thank you guys for help and thank you TheGeneral for showing me the benchmarking tools. Also, the array that was given as a parameter was a copy instead of a reference, as running through the loop manually showed me that the bubble sort was doing it's job and not sorting an already sorted array.
To solve your initial problem you just need to copy your arrays, you can do this easily with ToArray():
Creates an array from a IEnumerable.
ss += selectionsort(arr1.ToArray());
bs += bubblesort(arr1.ToArray());
However let's learn how to do a more reliable benchmark with BenchmarkDotNet:
BenchmarkDotNet Nuget
Official Documentation
Given
public class Sort
{
public static void BubbleSort(int[] arr1)
{
int n = arr1.Length;
for (int i = 0; i < n - 1; i++)
{
for (int j = 0; j < n - i - 1; j++)
{
if (arr1[j] > arr1[j + 1])
{
int tmp = arr1[j];
// swap tmp and arr[i] int tmp = arr[j];
arr1[j] = arr1[j + 1];
arr1[j + 1] = tmp;
}
}
}
}
public static void SelectionSort(int[] arr2)
{
int n = arr2.Length;
for (int x = 0; x < arr2.Length - 1; x++)
{
int minPos = x;
for (int y = x + 1; y < arr2.Length; y++)
{
if (arr2[y] < arr2[minPos])
minPos = y;
}
if (x != minPos && minPos < arr2.Length)
{
int temp = arr2[minPos];
arr2[minPos] = arr2[x];
arr2[x] = temp;
}
}
}
}
Benchmark code
[SimpleJob(RuntimeMoniker.Net50)]
[MemoryDiagnoser()]
public class SortBenchmark
{
private int[] data;
[Params(100, 1000)]
public int N;
[GlobalSetup]
public void Setup()
{
var r = new Random(42);
data = Enumerable
.Repeat(0, N)
.Select(i => r.Next(0, N))
.ToArray();
}
[Benchmark]
public void Bubble() => Sort.BubbleSort(data.ToArray());
[Benchmark]
public void Selection() => Sort.SelectionSort(data.ToArray());
}
Usage
static void Main(string[] args)
{
BenchmarkRunner.Run<SortBenchmark>();
}
Results
Method
N
Mean
Error
StdDev
Bubble
100
8.553 us
0.0753 us
0.0704 us
Selection
100
4.757 us
0.0247 us
0.0231 us
Bubble
1000
657.760 us
7.2581 us
6.7893 us
Selection
1000
300.395 us
2.3302 us
2.1796 us
Summary
What have we learnt? Your bubble sort code is slower ¯\_(ツ)_/¯
It looks like you're passing in the sorted array into Bubble Sort. Because arrays are passed by reference, the sort that you're doing on the array is editing the same contents of the array that will be eventually passed into bubble sort.
Make a second array and pass the second array into bubble sort.
I'm writing a program to find the largest palindromic number made from product of 3-digit numbers. Firstly,i Create a method which has ability to check if it is a palindromic number. Here is my code :
static int check(string input_number)
{
for (int i = 0; i < input_number.Length/2; i++)
if (input_number[i] != input_number[input_number.Length - i])
return 0;
return 1;
}
After that, it's my main code:
static void Main(string[] args)
{
int k = 0;
for (int i = 0; i < 999; i++)
for (int j = 0; j < 999; j++)
{
k = i * j;
if (check(k.ToString()) == 1)
Console.Write(k + " ");
}
}
But when it has a problem that the length of input_number is zero. So my code doesn't run right way. What can I do to solve the length of input_number?
You have a few bugs in your code:
1. 3-digit-numbers range from `100` to `999`, not from `0` to `998` as your loops currently do.
So your Main method should look like this:
static void Main(string[] args)
{
int k = 0;
for (int i = 100; i <= 999; i++)
for (int j = 100; j <= 999; j++)
{
k = i * j;
if (check(k.ToString()) == 1)
Console.Write(k + " ");
}
}
Now all pairs of three digit numbers are checked. But to improve performance you can let j start at i, because you already checked e.g. 213 * 416 and don't need to check 416 * 213 anymore:
for (int i = 100; i <= 999; i++)
for (int j = i; j <= 999; j++) // start at i
And since you want to find the largest, you may want to start at the other end:
for (int i = 999; i >= 100; i--)
for (int j = 999; j >= 100; j--)
But that still does not guarantee that the first result will be the largest. You need to collect the result and sort them. Here is my LINQ suggestion for your Main:
var results = from i in Enumerable.Range(100, 900)
from j in Enumerable.Range(i, 1000-i)
let k = i * j
where (check(k.ToString() == 1)
orderby k descending
select new {i, j, k};
var highestResult = results.FirstOrDefault();
if (highestResult == null)
Console.WriteLine("There are no palindromes!");
else
Console.WriteLine($"The highest palindrome is {highestResult.i} * {highestResult.j} = {highestResult.k}");
2. Your palindrome-check is broken
You compare the character at index i to input_number[input_number.Length - i], which will throw an IndexOutOfRangeException for i = 0. Strings are zero-based index, so index of the last character is Length-1. So change the line to
if (input_number[i] != input_number[input_number.Length - i - 1])
Finally, I suggest to make the check method of return type bool instead of int:
static bool check(string input_number)
{
for (int i = 0; i < input_number.Length/2; i++)
if (input_number[i] != input_number[input_number.Length - i - 1])
return false;
return true;
}
This seems more natural to me.
You can use method below. Because you are trying to find the largest number you start from 999 and head backwards, do multiplication and check if its palindrome.
private void FindProduct()
{
var numbers = new List<int>();
for (int i = 999; i > 99; i--)
{
for (int j = 999; j > 99; j--)
{
var product = i * j;
var productString = product.ToString();
var reversed = product.Reverse();
if (product == reversed)
{
numbers.Add(product);
}
}
}
Console.WriteLine(numbers.Max());
}
I have wanted to try some challenges on Codility and started from beginning. All assignments were relatively easy up to the one called MaxCounters. I do not believe that this one is especially hard although it is the first one marked as not painless.
I have read the task and started coding in C# language:
public static int[] maxPart(int N, int[] A){
int[] counters = new int[N];
for(int i = 0; i < A.Length; i++){
for(int j = 0; j < counters.Length; j++){
if(A[i] == counters[j] && (counters[j] >= 1 && counters[j] <= N )){
counters [j] = counters [j] + 1;
}
if(A[i] == N + 1 ){
int tmpMax = counters.Max ();
for(int h = 0; h < counters.Length; h++){
counters [h] = tmpMax;
}
}
}
}
return counters;
}
Having 3 loops of course makes it really slow, but lets leave it for later. My concern is how I understood this like this and all the other people see it like on this question here.
From the assignment's description.
it has 2 actions:
increase(X) − counter X is increased by 1,
max counter − all counters are set to the maximum value of any
counter.
which occur under conditions:
if A[K] = X, such that 1 ≤ X ≤ N, then operation K is increase(X),
if A[K] = N + 1 then operation K is max counter.
Both conditions are stated in the code above. Obviusly it is wrong but I am confused, and I do not know how could I understand it differently.
Why is this code wrong, what am I missing from task description?
One of the top rated answers looks like this:
public int[] solution(int N, int[] A) {
int[] result = new int[N];
int maximum = 0;
int resetLimit = 0;
for (int K = 0; K < A.Length; K++)
{
if (A[K] < 1 || A[K] > N + 1)
throw new InvalidOperationException();
if (A[K] >= 1 && A[K] <= N)
{
if (result[A[K] - 1] < resetLimit) {
result[A[K] - 1] = resetLimit + 1;
} else {
result[A[K] - 1]++;
}
if (result[A[K] - 1] > maximum)
{
maximum = result[A[K] - 1];
}
}
else
{
// inefficiency here
//for (int i = 0; i < result.Length; i++)
// result[i] = maximum;
resetLimit = maximum;
}
}
for (int i = 0; i < result.Length; i++)
result[i] = Math.max(resetLimit, result[i]);
return result;
}
This code results with 100% on Codility.
Question:
I would like to know how the author knew from the task to use result[A[K] - 1]? What would resetLimit represent?
Maybe I completely misunderstood the question due to my English I am not sure. I just can not go over it.
EDIT:
Based on my code provided, how did I misunderstood the assignment? Generally I am asking for explanation of the problem. Whether to explain what needs to be done, or take the code as correct result and provide and explanation why is this done this way?
In my opinion you somehow mixed the index of the counter (values in A) and the value of the counter (values in counter). So there is no magic in using A[i]-1 - it is the value X from the problem description (adjusted to 0-based index).
My naive approach would be, the way I understood the problem (I hope it makes clear, what your code is doing wrong):
public static int[] maxPart(int N, int[] A){
int[] counters = new int[N];
for(int i = 0; i < A.Length; i++){
int X=A[i];
if(X>=1 && X<=N){ // this encodes increment(X), with X=A[i]
counters [X-1] = counters [X-1] + 1; //-1, because our index is 0-based
}
if(X == N + 1 ){// this encodes setting all counters to the max value
int tmpMax = counters.Max ();
for(int h = 0; h < counters.Length; h++){
counters [h] = tmpMax;
}
}
}
}
return counters;
}
Clearly, this would be too slow as the complexity isO(n^2) with n=10^5 number of operations (length of the array A), in the case of the following operation sequence:
max counter, max counter, max counter, ....
The top rated solution solves the problem in a lazy manner and does not update all values explicitly every time a max counter operation is encountered, but just remembers which minimal value all counters must have after this operation in resetLimit. Thus, every time he must increment a counter, he looks up whether its value must be updated due to former max counter operations and makes up for all max counter operation he didn't execute on this counter
if(result[A[K] - 1] < resetLimit) {
result[A[K] - 1] = resetLimit + 1;
}
His solution runs in O(n) and is fast enough.
Here is my solution in JavaScript.
const maxCounters = (N, A) => {
for (let t = 0; t < A.length; t++) {
if (A[t] < 1 || A[t] > N + 1) {
throw new Error('Invalid input array A');
}
}
let lastMaxCounter = 0; // save the last max counter is applied to all counters
let counters = []; // counters result
// init values by 0
for (let i = 0; i < N; i++) {
counters[i] = 0;
}
let currentMaxCounter = 0; // save the current max counter each time any counter is increased
let maxApplied = false;
for (let j = 0; j < A.length; j++) {
const val = A[j];
if (1 <= val && val <= N) {
if (maxApplied && counters[val - 1] < lastMaxCounter) {
counters[val - 1] = lastMaxCounter;
}
counters[val - 1] = counters[val - 1] + 1;
if (currentMaxCounter < counters[val - 1]) {
currentMaxCounter = counters[val - 1];
}
} else if (val === N + 1) {
maxApplied = true;
lastMaxCounter = currentMaxCounter;
}
}
// apply the lastMaxCounter to all counters
for (let k = 0; k < counters.length; k++) {
counters[k] = counters[k] < lastMaxCounter ? lastMaxCounter : counters[k];
}
return counters;
};
Here is C# solution give me 100% score
public int[] solution(int N, int[] A) {
int[] operation = new int[N];
int max = 0, globalMax = 0;
foreach (var item in A)
{
if (item > N)
{
globalMax = max;
}
else
{
if (operation[item - 1] < globalMax)
{
operation[item - 1] = globalMax;
}
operation[item - 1]++;
if (max < operation[item - 1])
{
max = operation[item - 1];
}
}
}
for (int i = 0; i < operation.Length; i++)
{
if (operation[i] < globalMax)
{
operation[i] = globalMax;
}
}
return operation;
}
Here is a pretty elegant soulution in Swift:
public func solution(_ N : Int, _ A : inout [Int]) -> [Int] {
var globalMax = 0
var currentMax = 0
var maximums: [Int: Int] = [:]
for x in A {
if x > N {
globalMax = currentMax
continue
}
let newValue = max(maximums[x] ?? globalMax, globalMax) + 1
currentMax = max(newValue, currentMax)
maximums[x] = newValue
}
var result: [Int] = []
for i in 1...N {
result.append(max(maximums[i] ?? globalMax, globalMax))
}
return result
}
Try this Java snippet. Its more readable and neater, you don't need to worry about bounds check and might evacuate your first findings related to the more efficient approach you have found, btw the max is on the main forloop not causing any overhead.
public final int[] solution(int N, int[] A)
{
int condition = N + 1;
int currentMax = 0;
int lastUpdate = 0;
int[] counters = new int[N];
for (int i = 0; i < A.length; i++)
{
int currentValue = A[i];
if (currentValue == condition)
{
lastUpdate = currentMax;
}
else
{
int position = currentValue - 1;
if (counters[position] < lastUpdate)
{
counters[position] = lastUpdate + 1;
}
else
{
counters[position]++;
}
if (counters[position] > currentMax)
{
currentMax = counters[position];
}
}
}
for (int i = 0; i < N; i++)
{
if (counters[i] < lastUpdate)
{
counters[i] = lastUpdate;
}
}
return counters;
}
Inspired by Andy's solution, here is a solution in Python that is O(N + M) and gets a score of 100. The key is to avoid the temptation of updating all the counters every time A[K] > 5. Instead you keep track of a global max and reset an individual counter to global max just before you have to increment it. At the end, you set the remaining un-incremented counters to global max. See the comments in the code below:
def solution(N,A):
max = 0
global_max = 0
counters = [0] * N
for operation in A:
if operation > N:
#don't update counters.
#Just keep track of global max until you have to increment one of the counters.
global_max = max
else:
#now update the target counter with global max
if global_max > counters[operation - 1]:
counters[operation - 1] = global_max
#increment the target counter
counters[operation - 1] += 1
#update max after having incremented the counter
if counters[operation - 1] > max:
max = counters[operation - 1]
for i in range(N):
#if any counter is smaller than global max, it means that it was never
#incremented after the global_max was reset. Its value can now be updated
#to global max.
if counters[i] < global_max:
counters[i] = global_max
return counters
Here's a C# solution that gave me 100% score.
The idea is to simply not update the max counters on the spot but rather do it when you actually get to that counter, and then even out any counters that were not set to the max in another loop.
class Solution
{
public int[] solution(int N, int[] A)
{
var result = new int[N];
var latestMax = 0;
var currentMax = 0;
for (int i = 0; i < A.Length; i++)
{
var currentValue = A[i];
if (currentValue >= 1 && currentValue <= N)
{
if (result[currentValue - 1] < currentMax)
{
result[currentValue - 1] = currentMax;
}
result[currentValue - 1]++;
if (result[currentValue - 1] > latestMax)
{
latestMax = result[currentValue - 1];
}
}
else if (currentValue == N + 1)
{
currentMax = latestMax;
}
}
for (int i = 0; i < result.Length; i++)
{
if (result[i] < currentMax)
{
result[i] = currentMax;
}
}
return result;
}
}
I have list and I want a short and fast way to make one of its element as the first.
I have in mind to use the code below to select the 10th element and make it first. But looking for a better soln
tempList.Insert(0, tempList[10]);
tempList.RemoveAt(11);
if you don't mind the ordering of the rest, you actually could swap the two items at position 0 and 10, believe it's better than doing insertion and deletion:
var other = tempList[0];
tempList[0]=tempList[10];
tempList[10] = other;
and you can even make this an extension of List for ease of use, something like:
public static void Swap<T>(this List<T> list, int oldIndex, int newIndex)
{
// place the swap code here
}
There are some special cases when you could get better performance (for the sake of simplicity, I'm assuming that the value is always inserted before the position where it was taken from):
class Program
{
const int Loops = 10000;
const int TakeLoops = 10;
const int ItemsCount = 100000;
const int Multiplier = 500;
const int InsertAt = 0;
static void Main(string[] args)
{
var tempList = new List<int>();
var tempDict = new Dictionary<int, int>();
for (int i = 0; i < ItemsCount; i++)
{
tempList.Add(i);
tempDict.Add(i, i);
}
var limit = 0;
Stopwatch
sG = new Stopwatch(),
s1 = new Stopwatch(),
s2 = new Stopwatch();
TimeSpan
t1 = new TimeSpan(),
t2 = new TimeSpan();
for (int k = 0; k < TakeLoops; k++)
{
var takeFrom = k * Multiplier + InsertAt;
s1.Restart();
for (int i = 0; i < Loops; i++)
{
tempList.Insert(InsertAt, tempList[takeFrom]);
tempList.RemoveAt(takeFrom + 1);
}
s1.Stop();
t1 += s1.Elapsed;
s2.Restart();
for (int i = 0; i < Loops; i++)
{
var e = tempDict[takeFrom];
for (int j = takeFrom - InsertAt; j > InsertAt; j--)
{
tempDict[InsertAt + j] = tempDict[InsertAt + j - 1];
}
tempDict[InsertAt] = e;
}
s2.Stop();
t2 += s2.Elapsed;
if (s2.Elapsed > s1.Elapsed || limit == 0)
limit = takeFrom;
}
sG.Start();
for (int k = 0; k < TakeLoops; k++)
{
var takeFrom = k * Multiplier + InsertAt;
if (takeFrom >= limit)
{
for (int i = 0; i < Loops; i++)
{
tempList.Insert(InsertAt, tempList[takeFrom]);
tempList.RemoveAt(takeFrom + 1);
}
}
else
{
for (int i = 0; i < Loops; i++)
{
var e = tempDict[takeFrom];
for (int j = takeFrom - InsertAt; j > InsertAt; j--)
{
tempDict[InsertAt + j] = tempDict[InsertAt + j - 1];
}
tempDict[InsertAt] = e;
}
}
}
sG.Stop();
Console.WriteLine("List: {0}", t1);
Console.WriteLine("Dictionary: {0}", t2);
Console.WriteLine("Optimized: {0}", sG.Elapsed);
/***************************
List: 00:00:11.9061505
Dictionary: 00:00:08.9502043
Optimized: 00:00:08.2504321
****************************/
}
}
In the example above, a Dictionary<int,int> is being used to store the index of each element. You will get better results if the gap between insertAt and takeFrom is smaller. As this interval increases, the performance will degrade. I guess you might want to evaluate this gap and take the optimal branch based on it's value.
I have gone through my code and tried to make it as efficient as i can think possible. Still i can not get this program to run all the way through without freezing my box. The maximum amount of time that i have let it run without it freezing it was about 2 hours. I can not believe this did not execute in that amount of time.
Is it an issue with my computer or is this brute force method of solving this problem that inefficient.
What are some ways that i can avoid this type of inefficiency when writing methods in the future?
private void Form1_Load(object sender, EventArgs e)
{
ArrayList listOfAbundantNumbers = new ArrayList();
ArrayList listOfSums = new ArrayList();
long total = 0;
for (int i = 1; i < 20162; i++)
{
if (isAbundandt(i))
{
listOfAbundantNumbers.Add(i);
}
total+=i;
}
for (int i = 1; i < listOfAbundantNumbers.Count; i++)
{
for (int a = 0; a < listOfAbundantNumbers.Count; a++)
{
long temp1 = Convert.ToInt64(listOfAbundantNumbers[i]);
long temp2 = Convert.ToInt64(listOfAbundantNumbers[a]);
long num = temp1 + temp2;
if(listOfSums.Contains(num) == false)
{
listOfSums.Add(num);
}
}
}
for (int i = 1; i < listOfAbundantNumbers.Count; i++)
{
long temp1 = Convert.ToInt64(listOfAbundantNumbers[i]);
total -= temp1;
}
printLn(total + "");
}
private ArrayList divisorList(long input)
{
ArrayList divisors = new ArrayList();
for (long i = 2; i < Math.Round(Math.Sqrt(input),0,0); i++)
{
long temp = input % i;
if (temp == 0)
{
divisors.Add(i);
divisors.Add(input / i);
}
}
return divisors;
}
private Boolean isAbundandt(long input)
{
long sum = 0;
ArrayList divisor = divisorList(input);
for (int i = 0; i < divisor.Count; i++)
{
long temp1 = Convert.ToInt64(divisor[i]);
sum += temp1;
}
sum++;
if (sum > input)
{
return true;
}
return false;
}
Avoid using ArrayList, prefer generic collection such as List<Int64> because you conversion may be quite expensive.
There is a part in your program which is in O(n^3). It can be reduce to O(n^2) easily. Just replace the following code (O(n) because of List<T>.Contains):
if(listOfSums.Contains(num) == false)
{
listOfSums.Add(num);
}
by:
listOfSums.Add(num);
where listOfSums is now a HashSet<Int64> (it's used to have a collection without duplicates).
Moreover, you can reduce the time for populating the listOfSums by a factor of 2 by just noticing that if you exchange i and a, the sum is the same, then, only one of the two combinations is added to the listOfSums.
You can replace:
for (int i = 1; i < listOfAbundantNumbers.Count; i++)
for (int a = 0; a < listOfAbundantNumbers.Count; a++)
by;
for (int i = 1; i < listOfAbundantNumbers.Count; i++)
for (int a = 0; a <= i; a++)
The final code is:
private static void Main()
{
List<Int64> listOfAbundantNumbers = new List<Int64>();
HashSet<Int64> listOfSums = new HashSet<Int64>();
long total = 0;
for (int i = 1; i < 20162; i++)
{
if (isAbundandt(i))
{
listOfAbundantNumbers.Add(i);
}
total += i;
}
for (int i = 0; i < listOfAbundantNumbers.Count; i++)
for (int a = 0; a <= i; a++)
{
long temp1 = Convert.ToInt64(listOfAbundantNumbers[i]);
long temp2 = Convert.ToInt64(listOfAbundantNumbers[a]);
long num = temp1 + temp2;
listOfSums.Add(num);
}
for (int i = 1; i < listOfAbundantNumbers.Count; i++)
{
long temp1 = Convert.ToInt64(listOfAbundantNumbers[i]);
total -= temp1;
}
Console.WriteLine(total + "");
}
private static List<Int64> divisorList(long input)
{
List<Int64> divisors = new List<Int64>();
for (long i = 2; i < Math.Round(Math.Sqrt(input), 0, 0); i++)
{
long temp = input % i;
if (temp == 0)
{
divisors.Add(i);
divisors.Add(input / i);
}
}
return divisors;
}
private static Boolean isAbundandt(long input)
{
long sum = 0;
List<Int64> divisor = divisorList(input);
for (int i = 0; i < divisor.Count; i++)
{
long temp1 = divisor[i];
sum += temp1;
}
sum++;
if (sum > input)
{
return true;
}
return false;
}