I am currently working on a recursively parallel Quicksort extension function for the List class. The code below represents the most basic thread distribution criteria I've considered because it should be the simplest to conceptually explain. It branches to the depth of the base-2 logarithm of the number of detected processors, and proceeds sequentially from there. Thus, each CPU should get one thread with a (roughly) equal, large share of data to process, avoiding excessive overhead time. The basic sequential algorithm is provided for comparison.
public static class Quicksort
{
/// <summary>
/// Helper class to hold information about when to parallelize
/// </summary>
/// <attribute name="maxThreads">Maximum number of supported threads</attribute>
/// <attribute name="threadDepth">The depth to which new threads should
/// automatically be made</attribute>
private class ThreadInfo
{
internal int maxThreads;
internal int threadDepth;
public ThreadInfo(int length)
{
maxThreads = Environment.ProcessorCount;
threadDepth = (int)Math.Log(maxThreads, 2);
}
}
/// <summary>
/// Helper function to perform the partitioning step of quicksort
/// </summary>
/// <param name="list">The list to partition</param>
/// <param name="start">The starting index</param>
/// <param name="end">The ending index/param>
/// <returns>The final index of the pivot</returns>
public static int Partition<T>(this List<T> list, int start, int end) where T: IComparable
{
int middle = (int)(start + end) / 2;
// Swap pivot and first item.
var temp = list[start];
list[start] = list[middle];
list[middle] = temp;
var pivot = list[start];
var swapPtr = start;
for (var cursor = start + 1; cursor <= end; cursor++)
{
if (list[cursor].CompareTo(pivot) < 0)
{
// Swap cursor element and designated swap element
temp = list[cursor];
list[cursor] = list[++swapPtr];
list[swapPtr] = temp;
}
}
// Swap pivot with final lower item
temp = list[start];
list[start] = list[swapPtr];
list[swapPtr] = temp;
return swapPtr;
}
/// <summary>
/// Method to begin parallel quicksort algorithm on a Comparable list
/// </summary>
/// <param name="list">The list to sort</param>
public static void QuicksortParallel<T>(this List<T> list) where T : IComparable
{
if (list.Count < 2048)
list.QuicksortSequential();
else
{
var info = new ThreadInfo(list.Count);
list.QuicksortRecurseP(0, list.Count - 1, 0, info);
}
}
/// <summary>
/// Method to implement parallel quicksort recursion on a Comparable list
/// </summary>
/// <param name="list">The list to sort</param>
/// <param name="start">The starting index of the partition</param>
/// <param name="end">The ending index of the partition (inclusive)</param>
/// <param name="depth">The current recursive depth</param>
/// <param name="info">Structure holding decision-making info for threads</param>
private static void QuicksortRecurseP<T>(this List<T> list, int start, int end, int depth,
ThreadInfo info)
where T : IComparable
{
if (start >= end)
return;
int middle = list.Partition(start, end);
if (depth < info.threadDepth)
{
var t = Task.Run(() =>
{
list.QuicksortRecurseP(start, middle - 1, depth + 1, info);
});
list.QuicksortRecurseP(middle + 1, end, depth + 1, info);
t.Wait();
}
else
{
list.QuicksortRecurseS(start, middle - 1);
list.QuicksortRecurseS(middle + 1, end);
}
}
/// <summary>
/// Method to begin sequential quicksort algorithm on a Comparable list
/// </summary>
/// <param name="list">The list to sort</param>
public static void QuicksortSequential<T>(this List<T> list) where T : IComparable
{
list.QuicksortRecurseS(0, list.Count - 1);
}
/// <summary>
/// Method to implement sequential quicksort recursion on a Comparable list
/// </summary>
/// <param name="list">The list to sort</param>
/// <param name="start">The starting index of the partition</param>
/// <param name="end">The ending index of the partition (inclusive)</param>
private static void QuicksortRecurseS<T>(this List<T> list, int start, int end) where T : IComparable
{
if (start >= end)
return;
int middle = list.Partition(start, end);
// Now recursively sort the (approximate) halves.
list.QuicksortRecurseS(start, middle - 1);
list.QuicksortRecurseS(middle + 1, end);
}
}
As far as I understand, this methodology should produce a one-time startup cost, then proceed in sorting the rest of the data significantly faster than sequential method. However, the parallel method takes significantly more time than the sequential method, which actually increases as the load increases. Benchmarked at a list of ten million items on a 4-core CPU, the sequential method averages around 18 seconds to run to completion, while the parallel method takes upwards of 26 seconds. Increasing the allowed thread depth rapidly exacerbates the problem.
Any help in finding the performance hog is appreciated. Thanks!
The problem is CPU cache conflict, also known as "false sharing."
Unless the memory address of the pivot point happens to fall on a cache line, one of the threads will get a lock on the L1 or L2 cache and the other one will have to wait. This can make performance even worse than a serial solution. The problem is described well in this article:
...where threads use different objects but those objects happen to be close enough in memory that they fall on the same cache line, and the cache system treats them as a single lump that is effectively protected by a hardware write lock that only one core can hold at a time. [1,2] This causes real but invisible performance contention; whichever thread currently has exclusive ownership so that it can physically perform an update to the cache line will silently throttle other threads that are trying to use different (but, alas, nearby) data that sits on the same line.
(snip)
In most cases, the parallel code ran actually ran slower than the sequential code, and in no case did we get any better than a 42% speedup no matter how many cores we threw at the problem.
Grasping at straws.... you may be able to do better if you separate the list into two objects, and pin them in memory (or even just pad them within a struct) so they are far enough apart that they won't have a cache conflict.
Related
Note: There is quite a bit of text here but its additional information that may or may not be required. If I don't include this, then responders ask for more information or make suggestions for approaches that may not work, due to my entire implementation. If there is a better way to structure this so that all information is available, please let me know the best way to provide this supporting information. The gist is that I am trying to is here:
I am building a program that will read files and determine an organization (internal org code) to associate each file to, based on a predefined list of orgs and how often each org shows up in the file. The basic premise and current manual solution is:
Manual Process
Receive files in an unusual but overall consistent data format.
Each file has data pertaining to an organization's transactions (inbound and outbound types)
Each row in the file represents a single transaction; however, the fields are not always in the same order.
Goal is to determine which org "owns" the transactions (the entire file will be associated with this org at the end)
To be determined as the transaction "owner", the org in question must show up the same amount of times on each row (e.g. if Org1 shows up twice on Row 1 and once on Row 2, it can't be our Org due to inconsistency).
Nearly 100% of the time, the correctly associated Org has the highest frequency in the file, consistent across every row checked so programming this will give us a 99% solution. We can refine it further, if needed, once these rules are implemented
So far in my project, I have implemented a class with the following members:
My class thus far (summary of all logic at this point):
Properties/fields
_orgArray - list of orgs to look for in each file
_orgLength - length of org code (used for iterating over character in file line and creating a buffer of specified length to check if buffer value contains org)
Methods
GetDictIntersects - accepts two dictionaries and returns a dictionary containing keys and values that exist in both input dictionaries
GetLinesFromFile - iterator that reads a file and yields a single lines from the file each iteration
GetOrgCounts - accepts a line of text (returned from GetLinesFromFile) and returns a dictionary containing each Org and its count of occurrences in the line of text
ReduceOrgArray - accepts a two dictionaries containing Org Counts (returned by GetOrgCounts) as well as an array of Orgs. Returns a new array of orgs having removed any entries that did not have consistencies amongst them (using GetDictIntersects) as well as any orgs that did not have any values > 0 (indicating no occurrences in lines of text)
What the DetermineOrg method should do
To determine an Org for each file, I'm working on a method named DetermineOrg. My goal is to utilize the above methods and properties to
starts with org array containing all potential orgs
iterates the specified file (using GetLinesFromFile)
reduces org array by removing orgs that don't meet criteria on each line by comparing the current line's org counts (returned from GetOrgCounts) with the previous line's org counts (using GetDictIntersects and ReduceOrgArray)
Once file is iterated, if only a single Org remains in array, that is the return value
If multiple Orgs remain, Org with highest count is returned. If there is a tie, then Org (that tied with Max count) that shows up first in _orgArray is the return value
However, I can't seem to figure out the logic of how structure this so that I can recursively implement this. My code for the method alone in question (in case you don't want the entire class) is below. Hopefully someone can point out what I'm doing incorrectly and point me in the right direction:
Stuck on this method:
/// <summary>
/// Determine the appropriate organization for specified file
/// Recursively apply the following business rules:
/// <list type="number">
/// <item>Initial run uses all Orgs</item>
/// <item>Get Org Counts for each org on a line-by-line basis</item>
/// <item>Compare each line's Org Counts with the previous line's Org Counts, removing any orgs from potential org list that do not have same counts on both lines</item>
/// <item>After entire file has been read, determine a single Org by identifying which Org has the most occurences (highest value in dict)</item>
/// <item>In case of ties, class member org array lists order of precedence. Org with lowest index takes precedence.</item>
/// </list>
/// </summary>
/// <param name="filePath"><c>string</c> - file to be processed</param>
/// <param name="numLines"><c>int</c>:
/// Number of lines to be read from file in order to determine associated org.
/// Value less than 1 indicates to read the entire file.
/// Default value is -1 (read entire file to determine associated org).
/// </param>
/// <param name="orgArray"><c>string[]</c> representing potential orgs that file may be associated with</param>
/// <param name="streamReader"><c>StreamReader</c> stream to specified file (read-only)</param>
/// <param name="prevOrgCounts"><c>int</c> representing Org Counts for previous line of text</param>
/// <returns><c>string</c> - represents org that file is associated with</returns>
public static string DetermineOrg(string filePath, int numLines = -1, string[] orgArray = null, IEnumerable<string> streamReader = null, Dictionary<string, int> prevOrgCounts = null)
{
// base condition - no streamreader exists yet
if (streamReader == null)
{
streamReader = GetLinesFromFile(filePath, numLines);
// if no orgArray value is set, then use class member as starting value
if (orgArray == null)
{
orgArray = _orgArray;
}
}
else
{
// get org counts from iterator
foreach (string line in streamReader)
{
Dictionary<string, int> currentOrgCounts = GetOrgCounts(line, orgArray);
// if we have previous and current counts, then get reduce orgs
if (prevOrgCounts != null)
{
orgArray = ReduceOrgArray(currentOrgCounts, prevOrgCounts, orgArray);
}
else
{
}
}
}
// base condition - if no counts yet, then get counts from filePath
if (prevOrgCounts == null)
{
foreach (string line in GetLinesFromFile(filePath, numLines))
{
prevOrgCounts = GetOrgCounts(filePath, _orgArray);
}
}
}
Entire Class / All Code
The entire class can be found below.
using System;
namespace OrgProcessor
{
internal class OrgProcessor
{
/// <summary>
/// <c>string[]</c> containing list of orgs in order of precedence. In case of ties between counts, order with lowest index takes precedence in org determination.
/// </summary>
static string[] _orgArray = { "Org1", "Org2", "Org3"};
/// <summary>
/// Length of Org (is consistent amongst orgs as each org is an "org code" representing an org that can be found in a lookup table.
/// </summary>
static byte _orgLength = 4;
/// <summary>
/// Compare 2 dictionaries and return a dictionary containing only keys and values that exist in both dictionaries.
/// </summary>
/// <param name="dictionary1"><c>Dictionary<string, int></c></param>
/// <param name="dictionary2"><c>Dictionary<string, int></param>
/// <returns><c>Dictionary<string, int> - New dictionary containing key-value pairs that exist in both input dictionaries</returns>
public static Dictionary<string, int> GetDictIntersects(Dictionary<string, int> dictionary1, Dictionary<string, int> dictionary2)
{
// only return entries that exist in both dictionaries
Dictionary<string, int> returnDict = new Dictionary<string, int>();
foreach (dynamic key in dictionary1.Keys)
{
// ensure key exists in other dictionary's keys AND values match in both dictionaries
if (dictionary2.ContainsKey(key))
{
if (dictionary2[key] == dictionary1[key])
{
returnDict.Add(key, dictionary1[key]);
}
}
}
return returnDict;
}
/// <summary>
/// Iterator method returning file content, line by line
/// </summary>
/// <param name="filePath"><c>string</c> - path to file to read from</param>
/// <param name="numLines"><c>int</c> - number of lines to read from the file. Negative numbers will be interpreted as "All Lines". Default value is -1 (Read "All" lines)</param>
/// <returns><c>IEnumerable</c><<c>string</c>></returns>
public static IEnumerable<string> GetLinesFromFile(string filePath, int numLines = -1)
{
// TODO: Make more generic so iterator can take instructions to manipulate lines in file
// and optionally write to file (would need to write to temp, then delete orig and rename temp
// track lines iterated
int i = 0;
// create reader
using (StreamReader reader = new StreamReader(filePath))
{
// yield line if not reached end of file AND
// num lines is not specified (i == -1) OR
// i (num lines return) has not exceeded num lines specified
while (!reader.EndOfStream && (i < numLines || numLines == -1))
{
yield return reader.ReadLine();
i++;
}
}
}
/// <summary>
/// Get number of times each org occurs in specified text
/// </summary>
/// <param name="lineOfText"><c>string</c> of text to process</param>
/// <param name="orgArray"><c>string[]</c> containing orgs to be counted</param>
/// <returns><c>Dictionary<string, int></c> containing each Org and number of occurences in specified text</returns>
public static Dictionary<string, int> GetOrgCounts(string lineOfText, string[] orgArray)
{
// instantiate return value
Dictionary<string, int> orgCounts = new Dictionary<string, int>();
// get length of line of text as it will be referenced multiple times
int textLength = lineOfText.Length;
foreach (string org in orgArray)
{
//// set matchCount to 0
//// int matchCount = 0;
// since orgs are 4 characters long, iterate each character and compare with next 3 characters for each
for(int i = 0; i < textLength; i ++)
{
// get character at index
char c = lineOfText[i];
// calculate remaining characters
int remainingChars = textLength - i;
// char can only be part of an org if enough characters remain in lineOfText
if (remainingChars >= _orgLength)
{
// Get amount of chars that equals org length
string curCharBuffer = lineOfText.Substring(i, _orgLength);
// if characters match current org, then increment count dictionary
// or add to dict with count of 1
if (curCharBuffer == org)
{
if (orgCounts.ContainsKey(org))
{
orgCounts[org] += 1;
}
else
{
orgCounts.Add(org, 1);
}
// no need to evaluate other characters that were part of org so adjust loop incrementer to start next iteration after buffer
i += curCharBuffer.Length - 1;
}
}
}
//// orgCounts[org] = matchCount;
}
return orgCounts;
}
/// <summary>
/// Accepts 2 dictionaries (containing orgs and associated counts) and an array of strings (representing potential orgs) and returns a new string array having removed any "invalid" strings based on specified business rules:
/// <list type="number">
/// <item>If no orgs exist in array, return empty array</item>
/// </list>
/// </summary>
/// <param name="dictA"><c>Dictionary<string, int> containing orgs and counts</c></param>
/// <param name="dictB"><c>Dictionary<string, int> containing orgs and counts</c></param>
/// <param name="orgArray"><c>string[]</c> portential orgs</param>
/// <returns><c>string[]</c> - represents potential orgs</returns>
public static string[] ReduceOrgArray(Dictionary<string, int> dictA, Dictionary<string, int> dictB, string[] orgArray)
{
// base condition - if orgArray is empty, then return as is
if (orgArray.Length == 0)
{
return orgArray;
}
// Return value
List<string> remainingOrgs= new List<string>();
// business rules require that org counts be same from record to record
// we can rule out potential orgs by removing those with inconsistent counts amongst records
Dictionary<string, int> remainingDict = GetDictIntersects(dictA, dictB);
// Iterate over remaining orgs
// remove those that don't have matching keys in remaining dict (showing inconsistency amongst recs)
// remove those with 0 counts (org must exist on all rows)
foreach (string org in orgArray)
{
if (remainingDict.ContainsKey(org))
{
if (remainingDict[org] > 0)
{
remainingOrgs.Add(org);
}
}
}
// return remainingOrgs as array
return remainingOrgs.ToArray();
}
/// <summary>
/// Determine the appropriate organization for specified file
/// Recursively apply the following business rules:
/// <list type="number">
/// <item>Initial run uses all Orgs</item>
/// <item>Get Org Counts for each org on a line-by-line basis</item>
/// <item>Compare each line's Org Counts with the previous line's Org Counts, removing any orgs from potential org list that do not have same counts on both lines</item>
/// <item>After entire file has been read, determine a single Org by identifying which Org has the most occurences (highest value in dict)</item>
/// <item>In case of ties, class member org array lists order of precedence. Org with lowest index takes precedence.</item>
/// </list>
/// </summary>
/// <param name="filePath"><c>string</c> - file to be processed</param>
/// <param name="numLines"><c>int</c>:
/// Number of lines to be read from file in order to determine associated org.
/// Value less than 1 indicates to read the entire file.
/// Default value is -1 (read entire file to determine associated org).
/// </param>
/// <param name="orgArray"><c>string[]</c> representing potential orgs that file may be associated with</param>
/// <param name="streamReader"><c>StreamReader</c> stream to specified file (read-only)</param>
/// <param name="prevOrgCounts"><c>int</c> representing Org Counts for previous line of text</param>
/// <returns><c>string</c> - represents org that file is associated with</returns>
public static string DetermineOrg(string filePath, int numLines = -1, string[] orgArray = null, IEnumerable<string> streamReader = null, Dictionary<string, int> prevOrgCounts = null)
{
// base condition - no streamreader exists yet
if (streamReader == null)
{
streamReader = GetLinesFromFile(filePath, numLines);
// if no orgArray value is set, then use class member as starting value
if (orgArray == null)
{
orgArray = _orgArray;
}
}
else
{
// get org counts from iterator
foreach (string line in streamReader)
{
Dictionary<string, int> currentOrgCounts = GetOrgCounts(line, orgArray);
// if we have previous and current counts, then get reduce orgs
if (prevOrgCounts != null)
{
orgArray = ReduceOrgArray(currentOrgCounts, prevOrgCounts, orgArray);
}
else
{
}
}
}
// base condition - if no counts yet, then get counts from filePath
if (prevOrgCounts == null)
{
foreach (string line in GetLinesFromFile(filePath, numLines))
{
prevOrgCounts = GetOrgCounts(filePath, _orgArray);
}
}
}
}
}
If I understood your code correctly, you only keep track of the current list of candidates, but not the ocurrence count so you have nothing to compare to.
Here is my solution:
string[] orgs = { "ORG1", "ORG2", "ORG3"};
// This array will hold
// - either the number of times the corresponding organisation name occurs in each line
// - or 0 if the corresponding organisation name does not occur in any line
// or occurs with different count in some lines
var orgCounts = new int[orgs.Length];
var readLines = 0; // the number of lines read so far (used to recognize the first line)
foreach (var line in File.ReadLines(fileName))
{
for (var i = 0; i < orgs.Length; i++)
{
// count the occurrences of orgs[i] in the read line
var count = CountOccurrences(line, orgs[i]);
if (readLines == 0)
{
// first line: just remember the count of occurrences
orgCounts[i] = count;
}
else if (orgCounts[i] != count)
{
// mismatch, set count to 0
orgCounts[i] = 0;
}
readLines++;
}
}
// helper function to count the occurrences of toFind in str
int CountOccurrences(string str, string toFind)
{
var count = 0;
var index = str.IndexOf(toFind);
while (index >= 0)
{
count++;
index = str.IndexOf(toFind, index+1);
}
return count;
}
This is the solution that worked. This was basically the final piece to the puzzle and now everything fits together:
public static string DetermineOrg(string filePath)
{
// Initialize variables to store previous line's org counts and current list of potential orgs
Dictionary<string, int> previousLineOrgCounts = null;
string[] potentialOrgs = _orgArray;
// Iteratve over the file's lines using the GetLinesFromFile method
foreach(string line in GetLinesFromFile(filePath))
{
// Get the org counts for the current line
Dictionary<string, int> currentLineOrgCounts = GetOrgCounts(line, potentialOrgs);
// Check if this is not the first line of the file
if (previousLineOrgCounts != null)
{
// reduce the list of potential orgs using the current and previous line org counts
potentialOrgs = ReduceOrgArray(previousLineOrgCounts, currentLineOrgCounts, potentialOrgs);
}
// update the previous line or g counts for the next iteration
previousLineOrgCounts = currentLineOrgCounts;
}
// Check if only one org remains in the potential orgs list
if(potentialOrgs.Length == 1)
{
// return the remaining org
return potentialOrgs[0];
}
else
{
// Initialize variables to store the max org count and the org with the max count
int maxOrgCount = 0;
string orgWithMaxCount = null;
// Iterate over the remaining potential orgs
foreach (string org in potentialOrgs)
{
// Check if the current org has a higher count that the previous max count
if (previousLineOrgCounts[org] > maxOrgCount)
{
// update the max org count and the org with the max count
maxOrgCount = previousLineOrgCounts[org];
orgWithMaxCount = org;
}
}
// Return the org with the max count
return orgWithMaxCount;
}
}
I have a method that yields an IEnumerable, but it uses the yield keyword to return elements when executed. I don't always know how big the total collection is. It's kind of similar to the standard Fibonacci example when you go to Try .NET except that it will yield a finite number of elements. That being said, because there's no way of knowing how many elements it will return beforehand, it could keep yielding pretty much forever if there are too many.
When I looked for other questions about this topic here, one of the answers provided a clean LINQ query to randomly sample N elements from a collection. However, the assumption here was that the collection was static. If you go to the Try .NET website and modify the code to use the random sampling implementation from that answer, you will get an infinite loop.
public static void Main()
{
foreach (var i in Fibonacci().OrderBy(f => Guid.NewGuid()).Take(20))
{
Console.WriteLine(i);
}
}
The query tries to order all the elements in the returned IEnumerable, but to order all the elements it must first calculate all the elements, of which there are an infinite number, which means it will keep going on and on and never return an ordered collection.
So what would be a good strategyfor randomly sampling an IEnumerable with an unknown number of contained elements? Is it even possible?
If a sequence is infinite in length, you can't select N elements from it in less than infinite time where the chance of each element in the sequence being selected is the same.
However it IS possible to select N items from a sequence of unknown, but finite, length. You can do that using reservoir sampling.
Here's an example implementation:
/// <summary>
/// This uses Reservoir Sampling to select <paramref name="n"/> items from a sequence of items of unknown length.
/// The sequence must contain at least <paramref name="n"/> items.
/// </summary>
/// <typeparam name="T">The type of items in the sequence from which to randomly choose.</typeparam>
/// <param name="items">The sequence of items from which to randomly choose.</param>
/// <param name="n">The number of items to randomly choose from <paramref name="items"/>.</param>
/// <param name="rng">A random number generator.</param>
/// <returns>The randomly chosen items.</returns>
public static T[] RandomlySelectedItems<T>(IEnumerable<T> items, int n, System.Random rng)
{
// See http://en.wikipedia.org/wiki/Reservoir_sampling for details.
var result = new T[n];
int index = 0;
int count = 0;
foreach (var item in items)
{
if (index < n)
{
result[count++] = item;
}
else
{
int r = rng.Next(0, index + 1);
if (r < n)
result[r] = item;
}
++index;
}
if (index < n)
throw new ArgumentException("Input sequence too short");
return result;
}
This must still iterate over the entire sequence, however, so it does NOT work for an infinitely long sequence.
If you want to support input sequences longer than 2^31, you can use longs in the implementation like so:
public static T[] RandomlySelectedItems<T>(IEnumerable<T> items, int n, System.Random rng)
{
// See http://en.wikipedia.org/wiki/Reservoir_sampling for details.
var result = new T[n];
long index = 0;
int count = 0;
foreach (var item in items)
{
if (index < n)
{
result[count++] = item;
}
else
{
long r = rng.NextInt64(0, index + 1);
if (r < n)
result[r] = item;
}
++index;
}
if (index < n)
throw new ArgumentException("Input sequence too short");
return result;
}
Note that this implementation requires .Net 6.0 or higher because of the rng.NextInt64().
Also note that there's no point in making n long because you can't have an array that exceeds ~2^31 elements, so it wouldn't be possible to fill it. You could in theory fix that by returning multiple arrays, but I'll leave that as an exercise for the reader. ;)
I was asked a question to write a optimal program that would determine the total number of stops a elevator has taken to serve X number of people. Question description is as below.
There is a elevator in a building with M floors, this elevator can take a max of X people at a time or max of total weight Y. Given that a set of people has arrived and their weight and the floor they need to stop given how many stops has the elevator taken to serve all the people. Consider elevator serves in the first come first serve basis.
E.g. Let Array A be the weight of people to be considered
A[] = {60, 80, 40 }
Let Array B be the floors where person needs to be dropped respectively
B[] = {2, 3, 5}
Total building floors be 5,max allowed person in elevator be 2 at a time with max weight capacity being 200
For this example the elevator would take total of 5 stops floors ground, 2, 3,ground, 5 , ground
What would be the optimal code for this?
One of my solution is as below. Is there any other better solutions?
class Solution
{
/// <summary>
/// Return total stops used
/// </summary>
/// <param name="A">weight of people</param>
/// <param name="B">floors they need to get down</param>
/// <param name="M">total floors in the building</param>
/// <param name="X">Max people to carry at a time</param>
/// <param name="Y">max weight to carry at a time</param>
/// <returns></returns>
public int solution(int[] A, int[] B, int M, int X, int Y)
{
// write your code in C# 6.0 with .NET 4.5 (Mono)
int totalStops = 0;
long totalWeightPerRound = 0;
int maxPersonsCount = 0;
List<int> lstFloors = new List<int>();
int currPerson = 0;
bool startLift = false;
while (currPerson < A.Length)
{
if ((totalWeightPerRound + A[currPerson]) <= Y && (maxPersonsCount+1) <= X)
{
totalWeightPerRound += A[currPerson];
maxPersonsCount++;
lstFloors.Add(B[currPerson]);
if (currPerson == A.Length - 1)
startLift = true;
currPerson++;
}
else
{
startLift = true;
}
if (startLift)
{
totalStops += lstFloors.Distinct().Count() + 1;
lstFloors.Clear();
maxPersonsCount = 0;
totalWeightPerRound = 0;
startLift = false;
}
}
return totalStops;
}
}
Maybe a little off topic, but as someone said above, it`s a math not a programming question. To be on the safe side, you should construct a functional describing the cost function you want to minimize, add constraints to include boundary conditions and finally calculate the variation to get the extremum.
Putting it another way, it is a non trivial mathematical task and you should REALLY concentrate on getting the math right before even trying to write a line of code. To optimize means to get the optimal solution, not just some solution ;)
First of all, I'm not really sure if I have framed my question correctly, but what I'm looking for can be better explained by looking at the below visual representation:
I have a method which returns an int within the range of 0 and 360.
Now, for further manipulation, I would like to round? or get the closest match from the numbers which are offset by 30. So how can I achieve this. Also, is there a specific term for the function that I'm looking for?
You may also edit the question if you think it can be written better.
Best Regards,
Navik.
This should work for any list where the items are an equal distance apart (i.e. 30, 60, 90).
EDIT
I've updated the code to use AlexD's elegant solution so that it will work with lists of any 'step' value, and with any starting (or ending) value (i.e. it could start with a negative number, like: -20, -15, -10, -5, 0, 5, 10, 15, 20):
/// <summary>
/// Gets the value of the item in the list of
/// numbers that is closest to the given number
/// </summary>
/// <param name="number">Any number</param>
/// <param name="numbers">A list of numbers, sorted from lowest to highest,
/// where the difference between each item is the same</param>
/// <returns>The value of the list item closest to the given number</returns>
public static int GetClosestNumber(int number, List<int> numbers)
{
if (numbers == null) throw new ArgumentNullException("numbers");
if (numbers.Count == 0)
throw new
ArgumentException("There are no items to compare against.", "numbers");
if (numbers.Count == 1) return numbers[0]; // Short-circuit for single-item lists
var step = Math.Abs(numbers[1] - numbers[0]);
// Get closest number using a slight modification of AlexD's elegant solution
var closestNumber = (Math.Abs(number) + (step / 2)) / step *
step * (number < 0 ? -1 : 1);
// Ensure numbers is within min/max bounds of the list
return Math.Min(Math.Max(closestNumber, numbers[0]), numbers[numbers.Count - 1]);
}
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
I have the following to solve and I'm not sure how to approach this:
There are parking lots that are adjacent to each other and their placement resembles a straight line. Each parking lot has a value (profit) assigned to it. You can purchase as many lots as you want BUT they have to be adjacent to each other (in a contiguous set).
INPUT (THIS IS GIVEN/WHAT YOU WOULD TYPE IN):
Number of lots: 9
Value for each parking lot: ie: -5, 0, 7, -6, 4, 3, -5, 0, 2
Representation (for easier viewing)
Each box contains profit of a each lot:
OUTPUT:
Should be:
3 6 8
meaning:
3 - start lot #,
6 - ending lot #,
8 - total profit (7 - 6 + 4 + 3)
If there is more than one answer, program should write the one that contains the smallest number of parking lots. If there is still more than one possible answer, your program can write any of them.
Please help. thanks in advance.
EDIT:
I got it working:
/// <summary>
/// The problem 2.
/// </summary>
public class MySuperAwesomeClass
{
#region Constants and Fields
/// <summary>
/// The seq end.
/// </summary>
private static int seqEnd = -1;
/// <summary>
/// The seq start.
/// </summary>
private static int seqStart;
#endregion
// Quadratic maximum contiguous subsequence sum algorithm.
#region Public Methods and Operators
/// <summary>
/// The max sub sum 2.
/// </summary>
/// <param name="a">
/// The a.
/// </param>
/// <returns>
/// The max sub sum 2.
/// </returns>
public static int maxSumSub(int[] a)
{
int maxSum = 0;
for (int i = 0; i < a.Length; i++)
{
int thisSum = 0;
for (int j = i; j < a.Length; j++)
{
thisSum += a[j];
if (thisSum > maxSum)
{
maxSum = thisSum;
seqStart = i;
seqEnd = j;
}
}
}
return maxSum;
}
#endregion
#region Methods
/// <summary>
/// The main.
/// </summary>
private static void Main()
{
Console.WriteLine("Enter N:");
string stringInput = Console.ReadLine();
int[] a = new int[Convert.ToInt16(stringInput)];
Console.WriteLine("Enter profit values:");
for (int i = 0; i < Convert.ToInt16(stringInput); i++)
{
string value = Console.ReadLine();
a[i] = Convert.ToInt16(value);
}
int maxSum = maxSumSub(a);
Console.WriteLine(string.Format("{0} {1} {2}", seqStart, seqEnd, maxSum));
Console.ReadKey();
}
#endregion
}
Except I can't figure out this part:
If there is more than one answer, program should write the one that contains the smallest number of parking lots.
This is the classic Maximum subset sum problem. No code as this is homework, but here is the general solution. I'm sure you can find code online by searching the title if you still get stuck.
Make first/last index variables for the max subset. These will hold the parking spaces of our answer. 3 and 6 respectively in your example.
Make a sum variable for the sum of the max subset. This will hold the sum of our answer. 8 in your example.
Make another set of first/last/sum variables which we will be our "current" variables.
Start at the beginning of the parking spaces. Place the current first and current last variable at the start and update the sum.
Now you're going to loop through each parking space by moving the current last variable and updating the sum.
If the current sum is greater then the max-so-far sum, save all the current variables into the max-so-far variables.
If at any point our current sum dips into the negative or becomes zero, our subset is not helping us get a max anymore, so restart it by moving the current first to where current last is and resetting current sum to zero.
Once we get to the end return the max-so-far variables
Here's a hint for a way you can make the algorithm more efficient: look at how the totals from each end add up. For example, from what you provided, from the left side the totals would be -5, -5, 2, -4, 0, 3, -2, -2, 0, and from the right side they would be 2, 2, -3, 0, 4, -2, 5, 5, 0.