As part of a coding challenge in my class, we have to produce code to provide for 10 different tasks.
In this task, my goal is to make a linear search algorithm that searches for a particular item in an array, and displays its position(s) if found.
This is my current code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Linearsearch2
{
class Program
{
static void Main(string[] args)
{
var array = new int[] { 1, 31, 10, 9, 420, -5, 77, 420, 300, 99 }; //Sets up the array
var targetvalue = 77; //Establishes what number the search will attempt to find.
var targetpos = -1; //Establishes the position in the array of the target.
var targetnumber = 0; //Establishes the counter for the number of times the target appears.
bool found = false; //Decides wether to change the number or use a counter method.
var foundpositions = new int[] { }; //Establishes an array which will hold the positions of located items
for (var i = 1; i < array.Length; i++)
{
if (found == true && array[i] == targetvalue)
{
targetnumber = targetnumber + 1;
}
if (found == false && array[i] == targetvalue) //If the target value has not been found yet
{
foundpositions.Add(i); //This is the line i need help with. I dont know how to add a value to an array properly.
found = true;
}
}
if (targetpos != -1){ //If the target number was found
Console.WriteLine("The number " + targetvalue + " appeared " + targetnumber + " times, at positions " + foundpositions + "."); // Prints the final outcome.
}
else //If the target number was not found
{
Console.WriteLine("The number " + targetvalue + " did not appear in this array."); // Prints the final outcome.
}
}
}
}
The problem i need help with is with line 31, with
foundpositions.Add(i);
I do not know the line to properly add values to an array, and this is what seems to be causing the problem.
(In this line, i am attempting to add the current position of the search to an array that will be displayed later)
Thanks for the help. Also, if there any other obvious, glaring errors, pointing them out to would be appreciated.
The problem i need help with is with line 31, with
foundpositions.Add(i);
Arrays are not dynamic and they don't have an add() method. you could use a List instead.
replace this:
var foundpositions = new int[] { };
with this:
var foundpositions = new List<int>();
also, you don't seem to have done anything with this declared variable:
var targetpos = -1;
hence control will never go inside this if block:
if (targetpos != -1){ //If the target number was found
Console.WriteLine("The number " + targetvalue + " appeared " + targetnumber + " times, at positions " + foundpositions + "."); // Prints the final outcome.
}
in this task, my goal is to make a linear search algorithm that
searches for a particular item in an array, and displays its
position(s) if found.
as your code currently looks, there seems to be several errors. However, the example below will help you get started:
public static int LinearSearch(int[] items, int target)
{
if (items == null) throw new ArgumentNullException("argument items has null reference");
if (items.Length == 0) return -1; // return -1 if the item is not found
for (int i = 0; i < items.Length; i++)
if (items[i] == target) return i;
return -1; // return -1 if the item is not found
}
then simply call the LinearSearch within the main method passing in the required data and you're good to go.
don't forget to assign the returned value from LinearSearch into a variable or simply print it to console.
I am not sure WHY you are checking if the target numbers are found. If you want to get the indexes in the array of all the ints that equal the target int, then you could simply loop through the array and put the matches into a List of ints then return this list.
The count of this returned list will tell you how many matched the target and the list would contain the indexes of those matches. There does not appear to be any reason to check if the target was found while looping through the array. If the returned list is empty, then the target was not found. The code below used this approach. I hope I am not missing something.
private static void FindTargets() {
var array = new int[] { 1, 31, 10, 9, 420, -5, 77, 420, 300, 99, 1, 31, 10, 9, 420, -5, 77, 420, 300, 99 }; //Sets up the array
int target = 77;
List<int> foundTargets = GetPositions(target, array);
StringBuilder sb = new StringBuilder();
sb.Append("There are " + foundTargets.Count + " int(s) that match " + target + Environment.NewLine);
sb.Append("The array indexs for the target ints " + target + " are: ");
int count = 0;
foreach (int curInt in foundTargets) {
sb.Append(curInt);
if (count < foundTargets.Count - 1)
sb.Append(", ");
count++;
}
Console.WriteLine(sb.ToString());
}
private static List<int> GetPositions(int target, int[] intArray) {
List<int> foundTargets = new List<int>();
for (int i = 0; i < intArray.Length; i++) {
if (intArray[i] == target) {
foundTargets.Add(i);
}
}
return foundTargets;
}
Related
I am trying to implement Bucket Sort Algorithm in C# on Linked List , but I am getting In correct results. This function should take List and Node in argument and than insert that node at correct ascending order.
I have a list [4, 7, 12, 15] and I want new Node with data 20 to be Inserted in Ascending order such as this list becomes [4,7,12,15,20].But in result I am getting this list[4,7,12,20,15] Please Help me, What's wrong in it. I guess there is some problem inside while loop. All other functions called by this function are totally working perfect
static public void InsertionSort(LinkedList list, Node s)
{
int data = s.getData();
if (list.start.getNext() == null)
list.InsertAtEnd(data);
else
{
int key = 0;
Node temp = list.start;
while (temp.getNext() != null)
{
temp = temp.getNext();
if (data >= temp.getData())
key++;
}
list.InsertAt(key, data);
}
}
Derived Function (Main Function):
LinkedList list = new LinkedList();
int[] array = { 4, 7, 12, 15 };
for (int i = 0; i < array.Length; i++)
list.InsertAtEnd(array[i]);
System.Console.WriteLine("Before Insertion");
list.Display();
Node n = new Node(20);
InsertionSort(list, n);
System.Console.WriteLine("After Insertion");
list.Display();
Output:
1: Output of The Code
The setup in the beginning is wrong.
int[] array = { 4, 7, 12, 15 };
for (int i = 0; i < array.Length; i++)
list.InsertAtBegin(array[i]);
Because you go over the initial array and add all the items at the beginning, the resulting list is sorted in the wrong order.
To start with the correct sorting, either add at the end:
int[] array = { 4, 7, 12, 15 };
for (int i = 0; i < array.Length; i++)
list.InsertAtEnd(array[i]);
or go over the initial array in the other direction:
int[] array = { 4, 7, 12, 15 };
for (int i = array.length - 1; i >= 0; i--)
list.InsertAtBegin(array[i]);
Also, your insertion algorithm isn't optimal.
With a linked list, to get to a certain element in the list is expensive because you always have to travel from the start to the correct position.
Your code first tries to find the correct place to insert the new element and then calls another method InsertAt which will have to restart traveling the list from the beginning to find the same position again.
Instead, change your code so that you directly insert the new node as soon as you have found an element with a higher value.
Thanks to all of you who tried to help me. I have correct my code. Now It's producing 100% accurate result.
static public void InsertionSort(LinkedList list, int data)
{
Node n = new Node(data);
Node temp = list.start;
Node sNode = null;
while(temp.getNext() !=null)
{
if (data >= temp.getNext().getData())
sNode = temp.getNext();
temp = temp.getNext();
}
if (sNode == null)
list.InsertAtBegin(data);
else
list.InsertAfter(sNode,n);
}
I made some C# code that outputs a SortedDictionary <int index, list<int>values>> Where the index always starts with lowest int of the list<> that follows. Here is a short example of input (in reality the input is larger):
index- Values<>
2 - 2,3,6,7
3 - 3,5
5 - 5,7,9
11 - 11,12,12
Now I want to do some re-orderning in here. The values are linked indexes. I want to sort them so that connected indexes are grouped together, with no double indexes. That should result in output
2 - 2,3,5,6,7,9
11 - 11,12
Initially i had problems working with a sortedDictionary using a foreach while also decreasing the dictionary set size. I solved that, and now give an update of this problem description with my latest code. It doesnt use a foreach anymore, and some sorting problems seam to be fixed now, but as a side effect it became pretty complex and large. And i doubt if it should be so complex, or might be written shorter, more simple.
Each List i call a tree, where is thoe dictionary are trees
And Cursor or c i use like reading out text digit by digit from screen.
For the moment I put it in a small concept function code in a console app. Just to check if it all works. Testing is quite complex, since number sets can be complexly linked. So its not directly visible if you got lots of sets and a lots of number how they should be sorted. Therefore manually checking code validity and results isn't easy either.
While I am not yet sure if it now indeed does work for 100%. It seams to work better then before. But i think this code is far from perfect, as i walk the set of trees twice. With a pre-sort and a final-sort.
static SortedDictionary<int, List<int>> NewSort(SortedDictionary<int, List<int>> trees)
{
bool debugmode = false;
//pre sort
List<int> indexTree = new List<int>();
foreach (KeyValuePair<int, List<int>> tree in trees)
{
indexTree.Add(tree.Key);
}
for (int i = 0; i < indexTree.Count; i++)
{
int cursor = 1;
List<int> tree = new List<int>();
int index = indexTree[i];
tree = trees[index];
while ((tree !=null)&& (cursor<tree.Count) )
{
int c = tree[cursor ];
if (trees.ContainsKey(c))
{
if (trees[c] != null)
{
List<int> u = new List<int>();
u = trees[c];
tree.AddRange(u);
tree.Sort();
trees[index] = tree;
trees[c] = null;
}
}
cursor++;
}
}
for (int i = trees.Count; i > 0; i--)
{
int c = indexTree[i - 1];
if (trees[c] == null)
{ trees.Remove(indexTree[i - 1]); }
else
{
trees[c] = trees[c].Distinct().ToList(); //removing all duplicates
}
}
indexTree = new List<int>();
//resort.
foreach (KeyValuePair<int, List<int>> tree in trees)
{
indexTree.Add(tree.Key);
if(debugmode) Console.WriteLine("* " +DumpIntList(trees[tree.Key]));
}
for (int i = (indexTree.Count); i > 0; i--)
{
if (debugmode) Console.WriteLine(i.ToString());
List<int> tree = new List<int>();
tree = trees[indexTree[i-1]];
for (int j = 0; j < tree.Count; j++)
{
int c = tree[j];
for (int k = (i - 2); k > 0; k--)
{
List<int> compareTree = new List<int>();
compareTree = trees[indexTree[k]];
if (compareTree.IndexOf(c) != -1) // found !
{
if (debugmode) Console.Write("found " + c.ToString() + " from ");
if (debugmode) Console.WriteLine(DumpIntList(tree) + " in (" + DumpIntList(compareTree)+ ")");
tree.Remove(c); // or we would create a duplicate
compareTree.AddRange(tree);
compareTree = compareTree.Distinct().ToList(); //removing doubles again, doubles as side effect of merging
compareTree.Sort();
trees.Remove(indexTree[i - 1]);
trees[indexTree[k]] = compareTree;
}
}
}
}
return trees;
}
Maybe what i try to do inst that clear to some, what i try to do here is that i try to look if series have overlapping numbers, and if so merge them.
Where each series is always sorted and starts with the lowest number of that series. As I found recently this might be a version of the UnionFind problem. The problem also appears in Blob detection, and finding what web pages are link eachother in a set of webpages. (but my data is a strange set lab measurements).
The difficulty is that there are lots of series, and it might be not directly clear if they are connected
1-3-4
4-7-9
11-12
would result in 2 series :
1) 1-3-4-7-9
2) 11-12
But after you add series 12-3 then it would all become one series.
Some more test data :
2 - 2,3,5,6,7 // note my data is always ordered like this
5 - 5,7,9 // dictionary starts with lowest key
11 - 11,12,12,27,30,31 // each list inside a tree key
22 - 22,27 // is ordered low to high
23 - 23,25 // lowest int, equals the dict key.
28 - 28,30
34 - 34
Output using above function
2 - 2,3,5,6,7,9
11 - 11,12,22,27,28,30,31
23 - 23,25
34 - 34
So while the code seams to work now, I highly doubt its ideal code, i irritate the trees set twice. And so i wonder if better solutions are possible. It might also be that the code doesnt do what i hope it todo; as i'm still testing it.
Other than inverting an if to avoid 1 level of nesting, I don't yet see how LINQ can be used to improve the readability of this code block.
static SortedDictionary<int, List<int>> SortTree(SortedDictionary<int, List<int>> trees)
{
//SortedDictionary<int, List<int>> newtrees = new SortedDictionary<int, List<int>>();
if (trees.Count < 2) { return trees; } // dont process if ntrees contains 1 or 0 trees
foreach (KeyValuePair<int, List<int>> singletree in trees)
{
int cursor = 1;
bool nFinish = false;
List<int> n = singletree.Value;
if (n.Count <= 1) continue;
while (nFinish == false)
{
if (trees.ContainsKey(n[cursor]))
{
List<int> t = trees[n[cursor]]; // think of a screen cursor going over the list table
t.AddRange(n);
trees.Remove(n[cursor]);
n.Sort();
trees[singletree.Key] = n;
}
cursor++;
if (cursor != n.Count) continue;
nFinish = true;
}
}
return trees;
}
Well i decreased the size of the function and improved it.
It should now be a single irritation over all trees.
Unless someone knows a better answer, i think its "the" answer.
The code has been tested to work with larger sets, and i couldnt spot errors.
static SortedDictionary<int, List<int>> NewSort(SortedDictionary<int, List<int>> trees)
{
bool debugmode = false;
//pre sort
List<int> indexTree = new List<int>();
foreach (KeyValuePair<int, List<int>> tree in trees)
{
indexTree.Add(tree.Key);
if(debugmode) Console.WriteLine("* " +DumpIntList(trees[tree.Key]));
}
for (int i = (indexTree.Count); i > 0; i--)
{
if (debugmode) Console.WriteLine(i.ToString());
List<int> tree = new List<int>();
tree = trees[indexTree[i-1]];
for (int j = 0; j < tree.Count; j++)
{
int c = tree[j];
for (int k = (i - 2); k > -1; k--) // k can be 0 but i can minimally be 1
{
List<int> compareTree = new List<int>();
compareTree = trees[indexTree[k]]; // for loop > checking all trees
if (compareTree.IndexOf(c) != -1) // found !
{
if (debugmode) Console.Write("found " + c.ToString() + " from ");
if (debugmode) Console.WriteLine(DumpIntList(tree) + " in (" + DumpIntList(compareTree)+ ")");
// tree.Remove(c); // or we would create a duplicate
compareTree.AddRange(tree);
compareTree = compareTree.Distinct().ToList();
compareTree.Sort();
trees.Remove(indexTree[i - 1]);
trees[indexTree[k]] = compareTree;
j =tree.Count; //break from more checks. maybe dirty code but it increases speed
break; //break checking loop on all trees for current tree
}
}
}
}
return trees;
}
Here is your solution with test cases
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo
{
public class Example
{
public static void Main()
{
SortedDictionary<int, List<int>> tempRepositary = new SortedDictionary<int, List<int>>();
//test 1
tempRepositary.Add(2, new List<int>(new[] { 2, 3, 5, 6, 7 }));
tempRepositary.Add(5, new List<int>(new[] { 5, 7, 9 }));
tempRepositary.Add(11, new List<int>(new[] { 11, 12, 12, 27, 30, 31 }));
tempRepositary.Add(22, new List<int>(new[] { 22, 27 }));
tempRepositary.Add(23, new List<int>(new[] { 23, 25 }));
tempRepositary.Add(28, new List<int>(new[] { 28, 30 }));
tempRepositary.Add(34, new List<int>(new[] { 34 }));
//test 2
//tempRepositary.Add(2, new List<int>(new[] { 2,3,6,7 }));
//tempRepositary.Add(3, new List<int>(new[] { 3,5 }));
//tempRepositary.Add(5, new List<int>(new[] { 5,7,9 }));
//tempRepositary.Add(11, new List<int>(new[] { 11,12,12 }));
var refreshOne = SortTree(tempRepositary);
foreach (var item in refreshOne)
{
Console.Write("Key:" + item.Key + " ");
Console.WriteLine(string.Join(",", item.Value));
}
Console.ReadKey();
}
private static SortedDictionary<int, List<int>> SortTree(SortedDictionary<int, List<int>> trees)
{
if (trees.Count < 2) { return trees; } // dont process if ntrees contains 1 or 0 trees
SortedDictionary<int, List<int>> compressedTree
= new SortedDictionary<int, List<int>>();
var allKeys = trees.Keys.ToList();
var allValues = trees.Values.ToList();
for (int i = 0; i < allKeys.Count; i++)
{
var tempValues = allValues[i];
var tempMax = tempValues.Max();
for (int j = i + 1; j < allKeys.Count; )
{
if (tempMax >= allKeys[j])
{
tempValues.AddRange(allValues[j]);
allKeys.Remove(allKeys[j]);
allValues.Remove(allValues[j]);
//
tempMax = tempValues.Max();
continue;
}
j++;
}
compressedTree.Add(allKeys[i], tempValues.Distinct().OrderBy(i1 => i1).ToList());
}
return compressedTree;
}
}
}
I'm coding a MS word based register tool for my office project where application will do complex SR based on Nummer (title numbers, Keywords and Law name) and create a register for each input word file.
Currently application is coded and 90% completed, for latest change request by client, I need following to be added to application.
currently I'm having list of title numbers like
1, 2, 3, 3.1, 3.2,3.3,3.4,4,5,6, 7.1.1,7.1.2,7.1.3
The requirement is to sort them in correct order ascending and Rangify the near numbers.
For example form the above :
1,2,3
should be rangify as:
1-3
3.1,3.2,3.3,3.4
should be rangify as:
3.1-3.4
and
4,5,6
as
4-5
and
7.1.1,7.1.2,7.1.3
as
7.1.1-7.1.3
Ultimately in above list, items should be sequenced and Rangify as bellow :
1-3, 3.1-3.4, 4-6, 7.1.1-7.1.3
I tried separating items by number of levels and adding them to sorted list and checking distance and make them in to one test range yet that didn't work our for me)
Then with some googling I found following c# function yet this function is for integers only
IEnumerable<string> Rangify(IList<int> input) {
for (int i = 0; i < input.Count; ) {
var start = input[i];
int size = 1;
while (++i < input.Count && input[i] == start + size)
size++;
if (size == 1)
yield return start.ToString();
else if (size == 2) {
yield return start.ToString();
yield return (start + 1).ToString();
} else if (size > 2)
yield return start + " - " + (start + size - 1);
}
}
So can someone please instruct me to get a solution for this.
Thanks
You can do something like this :
private static List<string> SortTitleNums(List<string> titleNums)
{
// list that'll hold the result of current operation
List<string> result = new List<string>();
// sorts the input array
titleNums.Sort();
// field that will indicate start and end of a sequence
bool sequenceStarted = false;
for (int i = 0; i < titleNums.Count - 1; i++)
{
// checks if the value is greater than current value by 1 to find sequence
if (Convert.ToInt32(titleNums[i + 1].Replace(".", "")) - Convert.ToInt32(titleNums[i].Replace(".", "")) == 1)
// if sequence is found we add this value to the result list and change sequnceStarted field to true.
{ if (!sequenceStarted) { result.Add(titleNums[i] + "-"); sequenceStarted = true; } }
// if sequence is found and next value does not refer to the sequence than we append the record with current value and change
//value for sequenceStarted field to false. If sequence not found than we just add the number.
else if (sequenceStarted) { result[result.Count - 1] += titleNums[i]; sequenceStarted = false; } else result.Add(titleNums[i]);
}
return result;
}
Example of usage :
static void Main()
{
List<string> titleNums = new List<string>()
{
"1", "2", "6", "3", "3.1", "3.2", "3.3", "8", "7.1.1", "7.1.2", "8.1.1", "7.1.3", "7.2.1",
};
titleNums = SortTitleNums(titleNums);
foreach (var item in titleNums)
Console.WriteLine(item);
Console.ReadKey();
}
Output : "1-3", "3.1-3.3", "6", "7.1.1-7.1.3", "7.2.1", "8"
Length = input Long(can be 2550, 2880, 2568, etc)
List<long> = {618, 350, 308, 300, 250, 232, 200, 128}
The program takes a long value, for that particular long value we have to find the possible combination from the above list which when added give me a input result(same value can be used twice). There can be a difference of +/- 30.
Largest numbers have to be used most.
Ex:Length = 868
For this combinations can be
Combination 1 = 618 + 250
Combination 2 = 308 + 232 + 200 +128
Correct Combination would be Combination 1
But there should also be different combinations.
public static void Main(string[] args)
{
//subtotal list
List<int> totals = new List<int>(new int[] { 618, 350, 308, 300, 250, 232, 200, 128 });
// get matches
List<int[]> results = KnapSack.MatchTotal(2682, totals);
// print results
foreach (var result in results)
{
Console.WriteLine(string.Join(",", result));
}
Console.WriteLine("Done.");
}
internal static List<int[]> MatchTotal(int theTotal, List<int> subTotals)
{
List<int[]> results = new List<int[]>();
while (subTotals.Contains(theTotal))
{
results.Add(new int[1] { theTotal });
subTotals.Remove(theTotal);
}
if (subTotals.Count == 0)
return results;
subTotals.Sort();
double mostNegativeNumber = subTotals[0];
if (mostNegativeNumber > 0)
mostNegativeNumber = 0;
if (mostNegativeNumber == 0)
subTotals.RemoveAll(d => d > theTotal);
for (int choose = 0; choose <= subTotals.Count; choose++)
{
IEnumerable<IEnumerable<int>> combos = Combination.Combinations(subTotals.AsEnumerable(), choose);
results.AddRange(from combo in combos where combo.Sum() == theTotal select combo.ToArray());
}
return results;
}
public static class Combination
{
public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<T> elements, int choose)
{
return choose == 0 ?
new[] { new T[0] } :
elements.SelectMany((element, i) =>
elements.Skip(i + 1).Combinations(choose - 1).Select(combo => (new[] { element }).Concat(combo)));
}
}
I Have used the above code, can it be more simplified, Again here also i get unique values. A value can be used any number of times. But the largest number has to be given the most priority.
I have a validation to check whether the total of the sum is greater than the input value. The logic fails even there..
The algorithm you have shown assumes that the list is sorted in ascending order. If not, then you shall first have to sort the list in O(nlogn) time and then execute the algorithm.
Also, it assumes that you are only considering combinations of pairs and you exit on the first match.
If you want to find all combinations, then instead of "break", just output the combination and increment startIndex or decrement endIndex.
Moreover, you should check for ranges (targetSum - 30 to targetSum + 30) rather than just the exact value because the problem says that a margin of error is allowed.
This is the best solution according to me because its complexity is O(nlogn + n) including the sorting.
V4 - Recursive Method, using Stack structure instead of stack frames on thread
It works (tested in VS), but there could be some bugs remaining.
static int Threshold = 30;
private static Stack<long> RecursiveMethod(long target)
{
Stack<long> Combination = new Stack<long>(establishedValues.Count); //Can grow bigger, as big as (target / min(establishedValues)) values
Stack<int> Index = new Stack<int>(establishedValues.Count); //Can grow bigger
int lowerBound = 0;
int dimensionIndex = lowerBound;
long fail = -1 * Threshold;
while (true)
{
long thisVal = establishedValues[dimensionIndex];
dimensionIndex++;
long afterApplied = target - thisVal;
if (afterApplied < fail)
lowerBound = dimensionIndex;
else
{
target = afterApplied;
Combination.Push(thisVal);
if (target <= Threshold)
return Combination;
Index.Push(dimensionIndex);
dimensionIndex = lowerBound;
}
if (dimensionIndex >= establishedValues.Count)
{
if (Index.Count == 0)
return null; //No possible combinations
dimensionIndex = Index.Pop();
lowerBound = dimensionIndex;
target += Combination.Pop();
}
}
}
Maybe V3 - Suggestion for Ordered solution trying every combination
Although this isn't chosen as the answer for the related question, I believe this is a good approach - https://stackoverflow.com/a/17258033/887092(, otherwise you could try the chosen answer (although the output for that is only 2 items in set being summed, rather than up to n items)) - it will enumerate every option including multiples of the same value. V2 works but would be slightly less efficient than an ordered solution, as the same failing-attempt will likely be attempted multiple times.
V2 - Random Selection - Will be able to reuse the same number twice
I'm a fan of using random for "intelligence", allowing the computer to brute force the solution. It's also easy to distribute - as there is no state dependence between two threads trying at the same time for example.
static int Threshold = 30;
public static List<long> RandomMethod(long Target)
{
List<long> Combinations = new List<long>();
Random rnd = new Random();
//Assuming establishedValues is sorted
int LowerBound = 0;
long runningSum = Target;
while (true)
{
int newLowerBound = FindLowerBound(LowerBound, runningSum);
if (newLowerBound == -1)
{
//No more beneficial values to work with, reset
runningSum = Target;
Combinations.Clear();
LowerBound = 0;
continue;
}
LowerBound = newLowerBound;
int rIndex = rnd.Next(LowerBound, establishedValues.Count);
long val = establishedValues[rIndex];
runningSum -= val;
Combinations.Add(val);
if (Math.Abs(runningSum) <= 30)
return Combinations;
}
}
static int FindLowerBound(int currentLowerBound, long runningSum)
{
//Adjust lower bound, so we're not randomly trying a number that's too high
for (int i = currentLowerBound; i < establishedValues.Count; i++)
{
//Factor in the threshold, because an end aggregate which exceeds by 20 is better than underperforming by 21.
if ((establishedValues[i] - Threshold) < runningSum)
{
return i;
}
}
return -1;
}
V1 - Ordered selection - Will not be able to reuse the same number twice
Add this very handy extension function (uses a binary algorithm to find all combinations):
//Make sure you put this in a static class inside System namespace
public static IEnumerable<List<T>> EachCombination<T>(this List<T> allValues)
{
var collection = new List<List<T>>();
for (int counter = 0; counter < (1 << allValues.Count); ++counter)
{
List<T> combination = new List<T>();
for (int i = 0; i < allValues.Count; ++i)
{
if ((counter & (1 << i)) == 0)
combination.Add(allValues[i]);
}
if (combination.Count == 0)
continue;
yield return combination;
}
}
Use the function
static List<long> establishedValues = new List<long>() {618, 350, 308, 300, 250, 232, 200, 128, 180, 118, 155};
//Return is a list of the values which sum to equal the target. Null if not found.
List<long> FindFirstCombination(long target)
{
foreach (var combination in establishedValues.EachCombination())
{
//if (combination.Sum() == target)
if (Math.Abs(combination.Sum() - target) <= 30) //Plus or minus tolerance for difference
return combination;
}
return null; //Or you could throw an exception
}
Test the solution
var target = 858;
var result = FindFirstCombination(target);
bool success = (result != null && result.Sum() == target);
//TODO: for loop with random selection of numbers from the establishedValues, Sum and test through FindFirstCombination
I have been stumped on this one for a while. I want to take a List and order the list such that the Products with the largest Price end up in the middle of the list. And I also want to do the opposite, i.e. make sure that the items with the largest price end up on the outer boundaries of the list.
Imagine a data structure like this.. 1,2,3,4,5,6,7,8,9,10
In the first scenario I need to get back 1,3,5,7,9,10,8,6,4,2
In the second scenario I need to get back 10,8,6,4,2,1,3,5,7,9
The list may have upwards of 250 items, the numbers will not be evenly distributed, and they will not be sequential, and I wanted to minimize copying. The numbers will be contained in Product objects, and not simple primitive integers.
Is there a simple solution that I am not seeing?
Any thoughts.
So for those of you wondering what I am up to, I am ordering items based on calculated font size. Here is the code that I went with...
The Implementation...
private void Reorder()
{
var tempList = new LinkedList<DisplayTag>();
bool even = true;
foreach (var tag in this) {
if (even)
tempList.AddLast(tag);
else
tempList.AddFirst(tag);
even = !even;
}
this.Clear();
this.AddRange(tempList);
}
The Test...
[TestCase(DisplayTagOrder.SmallestToLargest, Result=new[]{10,14,18,22,26,30})]
[TestCase(DisplayTagOrder.LargestToSmallest, Result=new[]{30,26,22,18,14,10})]
[TestCase(DisplayTagOrder.LargestInTheMiddle, Result = new[] { 10, 18, 26, 30, 22, 14 })]
[TestCase(DisplayTagOrder.LargestOnTheEnds, Result = new[] { 30, 22, 14, 10, 18, 26 })]
public int[] CalculateFontSize_Orders_Tags_Appropriately(DisplayTagOrder sortOrder)
{
list.CloudOrder = sortOrder;
list.CalculateFontSize();
var result = (from displayTag in list select displayTag.FontSize).ToArray();
return result;
}
The Usage...
public void CalculateFontSize()
{
GetMaximumRange();
GetMinimunRange();
CalculateDelta();
this.ForEach((displayTag) => CalculateFontSize(displayTag));
OrderByFontSize();
}
private void OrderByFontSize()
{
switch (CloudOrder) {
case DisplayTagOrder.SmallestToLargest:
this.Sort((arg1, arg2) => arg1.FontSize.CompareTo(arg2.FontSize));
break;
case DisplayTagOrder.LargestToSmallest:
this.Sort(new LargestFirstComparer());
break;
case DisplayTagOrder.LargestInTheMiddle:
this.Sort(new LargestFirstComparer());
Reorder();
break;
case DisplayTagOrder.LargestOnTheEnds:
this.Sort();
Reorder();
break;
}
}
The appropriate data structure is a LinkedList because it allows you to efficiently add to either end:
LinkedList<int> result = new LinkedList<int>();
int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Array.Sort(array);
bool odd = true;
foreach (var x in array)
{
if (odd)
result.AddLast(x);
else
result.AddFirst(x);
odd = !odd;
}
foreach (int item in result)
Console.Write("{0} ", item);
No extra copying steps, no reversing steps, ... just a small overhead per node for storage.
C# Iterator version
(Very simple code to satisfy all conditions.)
One function to rule them all! Doesn't use intermediate storage collection (see yield keyword). Orders the large numbers either to the middle, or to the sides depending on the argument. It's implemented as a C# iterator
// Pass forward sorted array for large middle numbers,
// or reverse sorted array for large side numbers.
//
public static IEnumerable<long> CurveOrder(long[] nums) {
if (nums == null || nums.Length == 0)
yield break; // Nothing to do.
// Move forward every two.
for (int i = 0; i < nums.Length; i+=2)
yield return nums[i];
// Move backward every other two. Note: Length%2 makes sure we're on the correct offset.
for (int i = nums.Length-1 - nums.Length%2; i >= 0; i-=2)
yield return nums[i];
}
Example Usage
For example with array long[] nums = { 1,2,3,4,5,6,7,8,9,10,11 };
Start with forward sort order, to bump high numbers into the middle.
Array.Sort(nums); //forward sort
// Array argument will be: { 1,2,3,4,5,6,7,8,9,10,11 };
long[] arrLargeMiddle = CurveOrder(nums).ToArray();
Produces: 1 3 5 7 9 11 10 8 6 4 2
Or, Start with reverse sort order, to push high numbers to sides.
Array.Reverse(nums); //reverse sort
// Array argument will be: { 11,10,9,8,7,6,5,4,3,2,1 };
long[] arrLargeSides = CurveOrder(nums).ToArray();
Produces: 11 9 7 5 3 1 2 4 6 8 10
Significant namespaces are:
using System;
using System.Collections.Generic;
using System.Linq;
Note: The iterator leaves the decision up to the caller about whether or not to use intermediate storage. The caller might simply be issuing a foreach loop over the results instead.
Extension Method Option
Optionally change the static method header to use the this modifier public static IEnumerable<long> CurveOrder(this long[] nums) { and put it inside a static class in your namespace;
Then call the order method directly on any long[ ] array instance like so:
Array.Reverse(nums); //reverse sort
// Array argument will be: { 11,10,9,8,7,6,5,4,3,2,1 };
long[] arrLargeSides = nums.CurveOrder().ToArray();
Just some (unneeded) syntactic sugar to mix things up a bit for fun. This can be applied to any answers to your question that take an array argument.
I might go for something like this
static T[] SortFromMiddleOut<T, U>(IList<T> list, Func<T, U> orderSelector, bool largestInside) where U : IComparable<U>
{
T[] sortedArray = new T[list.Count];
bool add = false;
int index = (list.Count / 2);
int iterations = 0;
IOrderedEnumerable<T> orderedList;
if (largestInside)
orderedList = list.OrderByDescending(orderSelector);
else
orderedList = list.OrderBy(orderSelector);
foreach (T item in orderedList)
{
sortedArray[index] = item;
if (add)
index += ++iterations;
else
index -= ++iterations;
add = !add;
}
return sortedArray;
}
Sample invocations:
int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int[] sortedArray = SortFromMiddleOut(array, i => i, false);
foreach (int item in sortedArray)
Console.Write("{0} ", item);
Console.Write("\n");
sortedArray = SortFromMiddleOut(array, i => i, true);
foreach (int item in sortedArray)
Console.Write("{0} ", item);
With it being generic, it could be a list of Foo and the order selector could be f => f.Name or whatever you want to throw at it.
The fastest (but not the clearest) solution is probably to simply calculate the new index for each element:
Array.Sort(array);
int length = array.Length;
int middle = length / 2;
int[] result2 = new int[length];
for (int i = 0; i < array.Length; i++)
{
result2[middle + (1 - 2 * (i % 2)) * ((i + 1) / 2)] = array[i];
}
Something like this?
public IEnumerable<int> SortToMiddle(IEnumerable<int> input)
{
var sorted = new List<int>(input);
sorted.Sort();
var firstHalf = new List<int>();
var secondHalf = new List<int>();
var sendToFirst = true;
foreach (var current in sorted)
{
if (sendToFirst)
{
firstHalf.Add(current);
}
else
{
secondHalf.Add(current);
}
sendToFirst = !sendToFirst;
}
//to get the highest values on the outside just reverse
//the first list instead of the second
secondHalf.Reverse();
return firstHalf.Concat(secondHalf);
}
For your specific (general) case (assuming unique keys):
public static IEnumerable<T> SortToMiddle<T, TU>(IEnumerable<T> input, Func<T, TU> getSortKey)
{
var sorted = new List<TU>(input.Select(getSortKey));
sorted.Sort();
var firstHalf = new List<TU>();
var secondHalf = new List<TU>();
var sendToFirst = true;
foreach (var current in sorted)
{
if (sendToFirst)
{
firstHalf.Add(current);
}
else
{
secondHalf.Add(current);
}
sendToFirst = !sendToFirst;
}
//to get the highest values on the outside just reverse
//the first list instead of the second
secondHalf.Reverse();
sorted = new List<TU>(firstHalf.Concat(secondHalf));
//This assumes the sort keys are unique - if not, the implementation
//needs to use a SortedList<TU, T>
return sorted.Select(s => input.First(t => s.Equals(getSortKey(t))));
}
And assuming non-unique keys:
public static IEnumerable<T> SortToMiddle<T, TU>(IEnumerable<T> input, Func<T, TU> getSortKey)
{
var sendToFirst = true;
var sorted = new SortedList<TU, T>(input.ToDictionary(getSortKey, t => t));
var firstHalf = new SortedList<TU, T>();
var secondHalf = new SortedList<TU, T>();
foreach (var current in sorted)
{
if (sendToFirst)
{
firstHalf.Add(current.Key, current.Value);
}
else
{
secondHalf.Add(current.Key, current.Value);
}
sendToFirst = !sendToFirst;
}
//to get the highest values on the outside just reverse
//the first list instead of the second
secondHalf.Reverse();
return(firstHalf.Concat(secondHalf)).Select(kvp => kvp.Value);
}
Simplest solution - order the list descending, create two new lists, into the first place every odd-indexed item, into the other every even indexed item. Reverse the first list then append the second to the first.
Okay, I'm not going to question your sanity here since I'm sure you wouldn't be asking the question if there weren't a good reason :-)
Here's how I'd approach it. Create a sorted list, then simply create another list by processing the keys in order, alternately inserting before and appending, something like:
sortedlist = list.sort (descending)
biginmiddle = new list()
state = append
foreach item in sortedlist:
if state == append:
biginmiddle.append (item)
state = prepend
else:
biginmiddle.insert (0, item)
state = append
This will give you a list where the big items are in the middle. Other items will fan out from the middle (in alternating directions) as needed:
1, 3, 5, 7, 9, 10, 8, 6, 4, 2
To get a list where the larger elements are at the ends, just replace the initial sort with an ascending one.
The sorted and final lists can just be pointers to the actual items (since you state they're not simple integers) - this will minimise both extra storage requirements and copying.
Maybe its not the best solution, but here's a nifty way...
Let Product[] parr be your array.
Disclaimer It's java, my C# is rusty.
Untested code, but you get the idea.
int plen = parr.length
int [] indices = new int[plen];
for(int i = 0; i < (plen/2); i ++)
indices[i] = 2*i + 1; // Line1
for(int i = (plen/2); i < plen; i++)
indices[i] = 2*(plen-i); // Line2
for(int i = 0; i < plen; i++)
{
if(i != indices[i])
swap(parr[i], parr[indices[i]]);
}
The second case, Something like this?
int plen = parr.length
int [] indices = new int[plen];
for(int i = 0; i <= (plen/2); i ++)
indices[i] = (plen^1) - 2*i;
for(int i = 0; i < (plen/2); i++)
indices[i+(plen/2)+1] = 2*i + 1;
for(int i = 0; i < plen; i++)
{
if(i != indices[i])
swap(parr[i], parr[indices[i]]);
}