C# Finding Maximum Value of Items that fit in certain slots - c#

All,
I'm having an issue wrapping my head around how to find the ideal relationship between 2 values while also having to fit those items into a certain position. I honestly don't even know where to begin. I researched the knapsack problem, but there is no positional requirements.
Example:
I have $50.00 to spend on food. I need to eat 4 meals (breakfast, lunch, dinner, and a snack - which can be breakfast or lunch). Each meal has 4? properties; name, slot, calories, and cost. My goal is to eat the most calories while staying under my $50.00 allotment.
class Meal
{
public enum MealType
{
Breakfast = 1,
Lunch = 2,
Dinner = 3
}
public string Name { get; set; }
public MealType Type { get; set; }
public int Calories { get; set; }
public decimal Cost { get; set; }
public Meal(string _name, MealType _type, int _calories, decimal _cost)
{
Name = _name;
Type = _type;
Calories = _calories;
Cost = _cost;
}
}
I'm able to read in from a spreadsheet or some other source and create a Meal for each record and add it to a List. I have no idea how to read through my list (or any collection), though, and find the maximum caloric combination of 4 meals while adhering to the requirement that Meal #1 must be of type "breakfast", Meal #2 must be of type "lunch", Meal #3 must be of type "dinner", and Meal #4 can be of type "breakfast" or "lunch". Any ideas on where to begin? Thanks.
UPDATE
I ended up getting this to work - though I'm sure it is not very efficient, especially as the input list grows. Here is the hideous code (I obviously also defined a Menu class not included):
List<Meal> allMeals = Meal.GetMeals();
List<Meal> allBreakfast = new List<Meal>();
List<Meal> allLunch = new List<Meal>();
List<Meal> allDinner = new List<Meal>();
List<Meal> allSnack = new List<Meal>();
foreach (Meal meal in allMeals)
{
if (meal.MealType == Meal.MealType.Breakfast)
{
allBreakfast.Add(meal);
allSnack.Add(meal);
}
else if (meal.MealType == Meal.MealType.Lunch)
{
allLunch.Add(meal);
allSnack.Add(meal);
}
else if (meal.MealType == Meal.MealType.Dinner)
{
allDinner.Add(meal);
}
}
foreach (Meal breakfast in allBreakfast)
{
foreach (Meal lunch in allLunch)
{
foreach (Meal dinner in allDinner)
{
foreach (Meal snack in allSnack)
{
if (snack == breakfast || snack == lunch)
{
continue;
}
currMenu = new Menu(breakfast, lunch, dinner, snack);
if (currMenu.Cost < Menu.MaxCost && (maxMenu == null || currMenu.Calories > maxMenu.Calories))
{
maxMenu = currMenu;
}
}
}
}
}

In this situation, if you are not familiar with complex algorithm, you can try the brute force algo which is still feasible in the time and memory space.
We have 4 sets to store record: A, B, C, D (A: breakfast, B: lunch, C: dinner, D = A + B)
Our aim is to find Ai, Bj, Cu, Dv so as to:
1. Ai.cost + Bj.cost + Cu.cost + Dv.cost <=50
2. Ai.calo + Bj.calo + Cu.calo + Dv.calo is maximum
As in reality, we just count up to 2 decimal--> range of price is from 0.00 -> 50.00, i.e. 5000 case
1. Find all cost combination of Ai + Bj (<=50) store into array AB.
if (Ai1 + Bj1 == Ai2 + Bj2), just take combination have higher calories.
=> Complexity: O(|A| * |B|)
2. Find all cost combination of Cu + Dv (<=50) store into array CD with the same process as for AB
=> Complexity: O(|C| * |D|)
Note: size of AB and CD is 5000 elements
3. Find all combination AB[p] + CD[q] which cost is under 5000 and we can know which one got highest calories.
=> Complexity: O(|AB| * |CD| ) = O(5000 * 5000)
Totally, if using brute-force, the time complexity is O(5000 * 5000) + O(|spreadSheet size for one categories|^2).
If the spreadSheet size is <= 5000 and you are not familiar with algorithm, you can use this simple approach. Otherwise, just move ahead, algorithm is fun :)

Ok, so if you have read about Knapsack problem, and still don't know how to solve it, here is something for you to consider
The idea is simple, the array result contains all the value of money that can be reached by any Meal combination. So at first, when you doesn't buy anything, only result[50] can be reached.
Iterating through all the choices, starting from breakfast to snack, and only check for the value that can be reached, we can slowly build up the value table. For example, if breakfast has three prices(1,2,3) so the next state can only be (50-1), (50-2), (50-3) and so on. In array result, we store the maximum value of calories which can be created. So if one Breakfast set is cost 3, calo 2, and another set is cost 3, calo 4, so the value at index (50 -3) of the result array is 4.
Finally, after scanning through all of the state, the value storing in result array is the maximum value of calories that can be made.
int money = 50;// your initial money
int[] result = new int[money + 1];
for (int i = 0; i < result.length; i++) {
result[i] = -1;//-1 means cannot reach
}
result[50] = 0;//Only 50 can be reached at first
List<Meal>[] list = new List[4]; // Array for all 4 meals
// 0 is Breakfast
// 1 is lunch
// 2 for dinner
// 3 for snack
for (int i = 0; i < 4; i++) {//Iterating from breakfast to snack
int[] temp = new int[money + 1];//A temporary array to store value
for (int j = 0; j < result.length; j++) {
temp[j] = -1;
}
for (int j = 0; j < result.length; j++) {
if (result[j] != -1) {//For previous value, which can be reached
//Try all posibilites
for (int k = 0; k < list[i].size(); k++) {
int cost = list[i].get(k).cost;
int energy = list[i].get(k).energy;
if (cost <= result[j]) {
temp[j - cost] = max(temp[j - cost], result[j] + energy);
}
}
}
}
result = temp;//Need to switch these arrays.
}
int max = 0;
for(int i = 0; i < result.length; i++){
max = max(max, result[i]);
}
System.out.println(max);

Related

Algorithm to match buy and sell orders

I am building an online stock game. All orders are at exactly market price. There is no real "bidding", only straight buy and sell. So this should be easier. Is there an algorithm that tackles the following problem:
Different orders with different volume. For example, the following buy orders are made...
order A for 50 shares
order B for 25 shares
order C for 10 shares
order D for 5 shares
order E for 5 shares
order F for 30 shares
There is a sell order G for 100 shares.
I need to find the right combination of the above buy orders in a way that gets as close to 100 shares as possible, without going over....
The Knapsack algorithm would work, but the performance will degrade very fast with a large number of users and orders being made. Is there a more efficient way to do this?
EDIT:
Here is my modified knapsack algorithm:
static int KnapSack(int capacity, int[] weight, int itemsCount)
{
int[,] K = new int[itemsCount + 1, capacity + 1];
for (int i = 0; i <= itemsCount; ++i)
{
for (int w = 0; w <= capacity; ++w)
{
if (i == 0 || w == 0)
K[i, w] = 0;
else if (weight[i - 1] <= w)
K[i, w] = Math.Max(weight[i - 1] + K[i - 1, w - weight[i - 1]], K[i - 1, w]);
else
K[i, w] = K[i - 1, w];
}
}
return K[itemsCount, capacity];
}
The only problem is that it is really bad on performance when the numbers are high.
/*
Given array prices, return max profit w/ 1 buy & 1 sell
Ex. prices = [7,1,5,3,6,4] -> 5 (buy at $1, sell at $6)
For each, get diff b/w that & min value before, store max
Time: O(n)
Space: O(1)
*/
class Solution {
public:
int maxProfit(vector<int>& prices) {
int minValue = prices[0];
int maxDiff = 0;
for (int i = 1; i < prices.size(); i++) {
minValue = min(minValue, prices[i]);
maxDiff = max(maxDiff, prices[i] - minValue);
}
return maxDiff;
}
};
ref: https://github.com/neetcode-gh/leetcode/blob/main/cpp/neetcode_150/03_sliding_window/best_time_to_buy_and_sell_stock.cpp
I am still not very clear with the example you gave in the question description, for any knapsack problem we need 2 things capacity and profit. Here you just provided capacity.
Considering you just need to reach 100 as close as possible without worrying about the profit then it's much simple and can have multiple ways to do it.
One way is to just take all the bigger one which is smaller than the remaining capacity. If they are bigger than the remaining capacity then go to the next smaller one.
Time: O(NlogN) for sorting
Space: O(1)
function getMax(arr, maxCap) {
arr.sort((a, b) => b - a);
let index = 0;
let cap = 0;
while (cap !== maxCap && index < arr.length) {
const remainingCap = maxCap - cap;
if (remainingCap >= arr[index]) {
cap += arr[index];
}
index++;
}
}

Figure out max number of consecutive seats

I had an interviewer ask me to write a program in c# to figure out the max number of 4 members families that can sit consecutively in a venue, taking into account that the 4 members must be consecutively seated in one single row, with the following context:
N represents the number of rows availabe.
The Columns are labeled from the letter "A" to "K", purposely ommiting the letter "i" (in other words, {A,B,C,D,E,F,G,H,J,K})
M represents a list of reserved seats
Quick example:
N = 2
M = {"1A","2F","1C"}
Solution = 3
In the representation you can see that, with the reservations and the size given, only three families of 4 can be seated in a consecutive order.
How would you solve this? is it possible to not use for loops? (Linq solutions)
I got mixed up in the for loops when trying to deal with the reservations aray: My idea was to obtain all the reservations that a row has, but then I don't really know how to deal with the letters (Converting directly from letter to number is a no go because the missing "I") and you kinda need the letters to position the reserved sits anyway.
Any approach or insight on how to go about this problem would be nice.
Thanks in advance!
Here is another implementation.
I also tried to explain why certain things have been done.
Good luck.
private static int GetNumberOfAvailablePlacesForAFamilyOfFour(int numberOfRows, string[] reservedSeats)
{
// By just declaring the column names as a string of the characters
// we can query the column index by colulmnNames.IndexOf(char)
string columnNames = "ABCDEFGHJK";
// Here we transform the reserved seats to a matrix
// 1A 2F 1C becomes
// reservedSeatMatrix[0] = [0, 2] -> meaning row 1 and columns A and C, indexes 0 and 2
// reservedSeatMatrix[1] = [5] -> meaning row 2 and column F, index 5
List<List<int>> reservedSeatMatrix = new List<List<int>>();
for (int row = 0; row < numberOfRows; row++)
{
reservedSeatMatrix.Add(new List<int>());
}
foreach (string reservedSeat in reservedSeats)
{
int seatRow = Convert.ToInt32(reservedSeat.Substring(0, reservedSeat.Length - 1));
int seatColumn = columnNames.IndexOf(reservedSeat[reservedSeat.Length - 1]);
reservedSeatMatrix[seatRow - 1].Add(seatColumn);
}
// Then comes the evaluation.
// Which is simple enough to read.
int numberOfAvailablePlacesForAFamilyOfFour = 0;
for (int row = 0; row < numberOfRows; row++)
{
// Reset the number of consecutive seats at the beginning of a new row
int numberOfConsecutiveEmptySeats = 0;
for (int column = 0; column < columnNames.Length; column++)
{
if (reservedSeatMatrix[row].Contains(column))
{
// reset when a reserved seat is reached
numberOfConsecutiveEmptySeats = 0;
continue;
}
numberOfConsecutiveEmptySeats++;
if(numberOfConsecutiveEmptySeats == 4)
{
numberOfAvailablePlacesForAFamilyOfFour++;
numberOfConsecutiveEmptySeats = 0;
}
}
}
return numberOfAvailablePlacesForAFamilyOfFour;
}
static void Main(string[] args)
{
int familyPlans = GetNumberOfAvailablePlacesForAFamilyOfFour(2, new string[] { "1A", "2F", "1C" });
}
Good luck on your interview
As always, you will be asked how could you improve that? So you'd consider complexity stuff like O(N), O(wtf).
Underlying implementation would always need for or foreach. Just importantly, never do unnecessary in a loop. For example, if there's only 3 seats left in a row, you don't need to keep hunting on that row because it is not possible to find any.
This might help a bit:
var n = 2;
var m = new string[] { "1A", "2F", "1C" };
// We use 2 dimension bool array here. If it is memory constraint, we can use BitArray.
var seats = new bool[n, 10];
// If you just need the count, you don't need a list. This is for returning more information.
var results = new List<object>();
// Set reservations.
foreach (var r in m)
{
var row = r[0] - '1';
// If it's after 'H', then calculate index based on 'J'.
// 8 is index of J.
var col = r[1] > 'H' ? (8 + r[1] - 'J') : r[1] - 'A';
seats[row, col] = true;
}
// Now you should all reserved seats marked as true.
// This is O(N*M) where N is number of rows, M is number of columns.
for (int row = 0; row < n; row++)
{
int start = -1;
int length = 0;
for (int col = 0; col < 10; col++)
{
if (start < 0)
{
if (!seats[row, col])
{
// If there's no consecutive seats has started, and current seat is available, let's start!
start = col;
length = 1;
}
}
else
{
// If have started, check if we could have 4 seats.
if (!seats[row, col])
{
length++;
if (length == 4)
{
results.Add(new { row, start });
start = -1;
length = 0;
}
}
else
{
// // We won't be able to reach 4 seats, so reset
start = -1;
length = 0;
}
}
if (start < 0 && col > 6)
{
// We are on column H now (only have 3 seats left), and we do not have a consecutive sequence started yet,
// we won't be able to make it, so break and continue next row.
break;
}
}
}
var solution = results.Count;
LINQ, for and foreach are similar things. It is possible you could wrap the above into a custom iterator like:
class ConsecutiveEnumerator : IEnumerable
{
public IEnumerator GetEnumerator()
{
}
}
Then you could start using LINQ.
If you represent your matrix in simple for developers format, it will be easier. You can accomplish it either by dictionary or perform not so complex mapping by hand. In any case this will calculate count of free consecutive seats:
public static void Main(string[] args)
{
var count = 0;//total count
var N = 2; //rows
var M = 10; //columns
var familySize = 4;
var matrix = new []{Tuple.Create(0,0),Tuple.Create(1,5), Tuple.Create(0,2)}.OrderBy(x=> x.Item1).ThenBy(x=> x.Item2).GroupBy(x=> x.Item1, x=> x.Item2);
foreach(var row in matrix)
{
var prevColumn = -1;
var currColumn = 0;
var free = 0;
var div = 0;
//Instead of enumerating entire matrix, we just calculate intervals in between reserved seats.
//Then we divide them by family size to know how many families can be contained within
foreach(var column in row)
{
currColumn = column;
free = (currColumn - prevColumn - 1)/familySize;
count += free;
prevColumn = currColumn;
}
currColumn = M;
free = (currColumn - prevColumn - 1)/familySize;
count += free;
}
Console.WriteLine("Result: {0}", count);
}

Implementing Levenstein distance for reversed string combination?

I have an employees list in my application. Every employee has name and surname, so I have a list of elements like:
["Jim Carry", "Uma Turman", "Bill Gates", "John Skeet"]
I want my customers to have a feature to search employees by names with a fuzzy-searching algorithm. For example, if user enters "Yuma Turmon", the closest element - "Uma Turman" will return. I use a Levenshtein distance algorithm, I found here.
static class LevenshteinDistance
{
/// <summary>
/// Compute the distance between two strings.
/// </summary>
public static int Compute(string s, string t)
{
int n = s.Length;
int m = t.Length;
int[,] d = new int[n + 1, m + 1];
// Step 1
if (n == 0)
{
return m;
}
if (m == 0)
{
return n;
}
// Step 2
for (int i = 0; i <= n; d[i, 0] = i++)
{
}
for (int j = 0; j <= m; d[0, j] = j++)
{
}
// Step 3
for (int i = 1; i <= n; i++)
{
//Step 4
for (int j = 1; j <= m; j++)
{
// Step 5
int cost = (t[j - 1] == s[i - 1]) ? 0 : 1;
// Step 6
d[i, j] = Math.Min(
Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1),
d[i - 1, j - 1] + cost);
}
}
// Step 7
return d[n, m];
}
}
I iterate user's input (full name) over the list of employee names and compare distance. If it is below 3, for example, I return found employee.
Now I want allow users to search by reversed names - for example, if user inputs "Turmon Uma" it will return "Uma Turman", as actually real distance is 1, because First name and Last name is the same as Last name and First name. My algorithm now counts it as different strings, far away. How can I modify it so that names are found regardless of order?
You can create a reversed version of employee names with LINQ. For example, if you have a list of employees like
x = ["Jim Carry", "Uma Turman", "Bill Gates", "John Skeet"]
you can write the following code:
var reversedNames = x.Select(p=> $"{p.Split(' ')[1] p.Split(' ')[0]}");
It will return the reversed version, like:
xReversed = ["Carry Jim", "Turman Uma", "Gates Bill", "Skeet John"]
Then repeat you algorithm with this data too.
A few thoughts, as this is a potentially complicated problem to get right:
Split each employee name into a list of strings. Personally, I'd probably discard anything with 2 or fewer letters, unless that's all the name is composed of. This should help with surnames like "De La Cruz" which might get searched as "dela cruz". Store the list of names for each employee in a dictionary that points back to that employee.
Split the search terms in the same way you split the names in the list. For each search term find the names with the lowest Levenshtein distance, then for each one, starting at the lowest, repeat the search with the rest of the search terms against the other names for that employee. Repeat this step with each word in the query. For example, if the query is John Smith, find the best single word name matches for John, then match remaining names for those "best match" employees on Smith, and get a sum of the distances. Then find the best matches for Smith and match remaining names on John, and sum the distances. The best match is the one with the lowest total distance. You can provide a list of best matches by returning the top 10, say, sorted by total distance. And it won't matter which way around the names in the database or the search terms are. In fact they could be completely out of order and it wouldn't matter.
Consider how to handle hyphenated names. I'd probably split them as if they were not hyphenated.
Consider upper/lower case characters, if you haven't already. You should store lookups in one case and convert the search terms to the same case before comparison.
Be careful of accented letters, many people have them in their names, such as รก. Your algorithm won't work correctly with them. Be even more careful if you expect to ever have non-alpha double byte characters, eg. Chinese, Japanese, Arabic, etc.
Two more benefits of splitting the names of each employee:
"Unused" names won't count against the total, so if I only search using the last name, it won't count against me in finding the shortest distance.
Along the same lines, you could apply some extra rules to help with finding non-standard names. For example, hyphenated names could be stored both as hyphenated (eg. Wells-Harvey), compound (WellsHarvey) and individual names (Wells and Harvey separate), all against the same employee. A low-distance match on any one name is a low-distance match on the employee, again extra names don't count against the total.
Here's some basic code that seems to work, however it only really takes into account points 1, 2 and 4:
using System;
using System.Collections.Generic;
using System.Linq;
namespace EmployeeSearch
{
static class Program
{
static List<string> EmployeesList = new List<string>() { "Jim Carrey", "Uma Thurman", "Bill Gates", "Jon Skeet" };
static Dictionary<int, List<string>> employeesById = new Dictionary<int, List<string>>();
static Dictionary<string, List<int>> employeeIdsByName = new Dictionary<string, List<int>>();
static void Main()
{
Init();
var results = FindEmployeeByNameFuzzy("Umaa Thurrmin");
// Returns:
// (1) Uma Thurman Distance: 3
// (0) Jim Carrey Distance: 10
// (3) Jon Skeet Distance: 11
// (2) Bill Gates Distance: 12
Console.WriteLine(string.Join("\r\n", results.Select(r => $"({r.Id}) {r.Name} Distance: {r.Distance}")));
var results = FindEmployeeByNameFuzzy("Tormin Oma");
// Returns:
// (1) Uma Thurman Distance: 4
// (3) Jon Skeet Distance: 7
// (0) Jim Carrey Distance: 8
// (2) Bill Gates Distance: 9
Console.WriteLine(string.Join("\r\n", results.Select(r => $"({r.Id}) {r.Name} Distance: {r.Distance}")));
Console.Read();
}
private static void Init() // prepare our lists
{
for (int i = 0; i < EmployeesList.Count; i++)
{
// Preparing the list of names for each employee - add special cases such as hyphenation here as well
var names = EmployeesList[i].ToLower().Split(new char[] { ' ' }).ToList();
employeesById.Add(i, names);
// This is not used here, but could come in handy if you want a unique index of names pointing to employee ids for optimisation:
foreach (var name in names)
{
if (employeeIdsByName.ContainsKey(name))
{
employeeIdsByName[name].Add(i);
}
else
{
employeeIdsByName.Add(name, new List<int>() { i });
}
}
}
}
private static List<SearchResult> FindEmployeeByNameFuzzy(string query)
{
var results = new List<SearchResult>();
// Notice we're splitting the search terms the same way as we split the employee names above (could be refactored out into a helper method)
var searchterms = query.ToLower().Split(new char[] { ' ' });
// Comparison with each employee
for (int i = 0; i < employeesById.Count; i++)
{
var r = new SearchResult() { Id = i, Name = EmployeesList[i] };
var employeenames = employeesById[i];
foreach (var searchterm in searchterms)
{
int min = searchterm.Length;
// for each search term get the min distance for all names for this employee
foreach (var name in employeenames)
{
var distance = LevenshteinDistance.Compute(searchterm, name);
min = Math.Min(min, distance);
}
// Sum the minimums for all search terms
r.Distance += min;
}
results.Add(r);
}
// Order by lowest distance first
return results.OrderBy(e => e.Distance).ToList();
}
}
public class SearchResult
{
public int Distance { get; set; }
public int Id { get; set; }
public string Name { get; set; }
}
public static class LevenshteinDistance
{
/// <summary>
/// Compute the distance between two strings.
/// </summary>
public static int Compute(string s, string t)
{
int n = s.Length;
int m = t.Length;
int[,] d = new int[n + 1, m + 1];
// Step 1
if (n == 0)
{
return m;
}
if (m == 0)
{
return n;
}
// Step 2
for (int i = 0; i <= n; d[i, 0] = i++)
{
}
for (int j = 0; j <= m; d[0, j] = j++)
{
}
// Step 3
for (int i = 1; i <= n; i++)
{
//Step 4
for (int j = 1; j <= m; j++)
{
// Step 5
int cost = (t[j - 1] == s[i - 1]) ? 0 : 1;
// Step 6
d[i, j] = Math.Min(
Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1),
d[i - 1, j - 1] + cost);
}
}
// Step 7
return d[n, m];
}
}
}
Simply call Init() when you start, then call
var results = FindEmployeeByNameFuzzy(userquery);
to return an ordered list of the best matches.
Disclaimers: This code is not optimal and has only been briefly tested, doesn't check for nulls, could explode and kill a kitten, etc, etc. If you have a large number of employees then this could be very slow. There are several improvements that could be made, for example when looping over the Levenshtein algorithm you could drop out if the distance gets above the current minimum distance.

Finding largest product in c#

Currently I am trying to find the thirteen adjacent digits in a 1000-digit number that will have the greatest product.Now I written a function which is supposed to multiply the desired number of adjacent digits to be multiplied and later store the product in a list . The two parameters the functions takes are the desired number of adjacent digits and string which contains the number. But for some reason it wont stop running.
public static void giveProduct(int quantity, string word)
{
int product = 1;
int place1 = 0;
int place2 = quantity - 1;
int temp = 1;
string temp2;
while (place2 < word.Length)
{
for (int i = place1; i < place2; i++)
{
temp2 = word[i].ToString();
temp = Int32.Parse(temp2);
product = product * i;
}
products.Add(product);
product = 1;
place1 += quantity;
place2 += quantity;
}
}
Can't reproduce your issue, the method terminates "correctly" for any sensible input.
But anyway, that is far from the only issue in your implementation. Your method is not calculating correctly the maximum product because you are skipping through the string quantity characters at a time. You should be skipping one character at a time and taking the quantity long substring starting at that position.
For string 123456 and quantity 3 you are evaluating 123 and 456. You should be checking 123, 234, 345, etc.
Also, get into the habit of:
Validating inputs
Writing helper methods. The shorter a method, the harder to introduce a bug in it.
Consider all possible values that word can represent? Have you considered { 1234 }? (note the leading and trailing spaces). How about -1234?
Prepare for the worse. Make your code robust so its able to handle incorrect data; your program will crash if the input is 123$5.
With all that in mind, consider the following implementation:
First a simple helper method that evaluates the product of all the digits of a given string representing a number.
private static bool TryMultiplyDigits(string number, out int product)
{
Debug.Assert(number != null && number.Length > 0);
product = 1;
foreach (var c in number)
{
int digit;
if (int.TryParse(c.ToString(), out digit))
{
product *= digit;
}
else
return false;
}
return true;
}
Ok great, this method will give us the correct product or simply tell us it can't evaluate it for any input.
Now, a method that will create all the possible subtrings and return the maximum product found:
public static int GetMaximumProduct(string number, int quantity)
{
if (number == null)
throw new ArgumentNullException(nameof(number));
if (quantity < 1)
throw new ArgumentOutOfRangeException(nameof(quantity));
if (quantity > number.Length)
throw new ArgumentException($"{nameof(quantity)} can not be greater than the length of {nameof(number)}.");
var normalizedNumber = number.Trim();
normalizedNumber = normalizedNumber.StartsWith("-") ? normalizedNumber.Substring(1) : normalizedNumber;
if (string.IsEmpty(normalizedNumber))
{
product = 0;
return true;
}
var maximumProduct = 0;
for (var i = 0; i < normalizedNumber.Length - (quantity - 1); i++)
{
int currentProduct;
if (TryMultiplyDigits(normalizedNumber.Substring(i, quantity), out currentProduct))
{
if (currentProduct > maximumProduct)
{
maximumProduct = currentProduct;
}
}
else
{
throw new FormatException("Specified number does not have the correct format.");
}
}
return maximumProduct;
}
And we're done!

Calculate all possible permutations/combinations, then check if the result is equal to a value

Best way I can explain it is using an example:
You are visiting a shop with $2000, your goal is to have $0 at the end of your trip.
You do not know how many items are going to be available, nor how much they cost.
Say that there are currently 3 items costing $1000, $750, $500.
(The point is to calculate all possible solutions, not the most efficient one.)
You can spend $2000, this means:
You can buy the $1000 item 0, 1 or 2 times.
You can buy the $750 item 0, 1 or 2 times.
You can buy the $500 item 0, 1, 2, 3 or 4 times.
At the end I need to be able to have all solutions, in this case it will be
2*$1000
1*$1000 and 2*$500
2*$750 and 1*$500
4*$500
Side note: you can't have a duplicate solution (like this)
1*$1000 and 2*$500
2*$500 and 1*$1000
This is what I tried:
You first call this function using
goalmoney = convert.ToInt32(goalMoneyTextBox.Text);
totalmoney = Convert.ToInt32(totalMoneyTextBox.Text);
int[] list = new int[usingListBox.Items.Count];
Calculate(0, currentmoney, list);
The function:
public void Calculate(int level, int money, int[] list)
{
string item = usingListBox.Items[level].ToString();
int cost = ItemDict[item];
for (int i = 0; i <= (totalmoney / cost); i++)
{
int[] templist = list;
int tempmoney = money - (cost * i);
templist[level] = i;
if (tempmoney == goalmoney)
{
resultsFound++;
}
if (level < usingListBox.Items.Count - 1 && tempmoney != goalmoney) Calculate(level + 1, tempmoney, templist);
}
}
Your problem can be reduced to a well known mathematical problem labeled Frobenius equation which is closely related to the well known Coin problem. Suppose you have N items, where i-th item costs c[i] and you need to spent exactly S$. So you need to find all non negative integer solutions (or decide whether there are no solutions at all) of equation
c[1]*n[1] + c[2]*n[2] + ... + c[N]*n[N] = S
where all n[i] are unknown variables and each n[i] is the number of bought items of i-th type.
This equation can be solved in a various ways. The following function allSolutions (I suppose it can be additionally simplified) finds all solutions of a given equation:
public static List<int[]> allSolutions(int[] system, int total) {
ArrayList<int[]> all = new ArrayList<>();
int[] solution = new int[system.length];//initialized by zeros
int pointer = system.length - 1, temp;
out:
while (true) {
do { //the following loop can be optimized by calculation of remainder
++solution[pointer];
} while ((temp = total(system, solution)) < total);
if (temp == total && pointer != 0)
all.add(solution.clone());
do {
if (pointer == 0) {
if (temp == total) //not lose the last solution!
all.add(solution.clone());
break out;
}
for (int i = pointer; i < system.length; ++i)
solution[i] = 0;
++solution[--pointer];
} while ((temp = total(system, solution)) > total);
pointer = system.length - 1;
if (temp == total)
all.add(solution.clone());
}
return all;
}
public static int total(int[] system, int[] solution) {
int total = 0;
for (int i = 0; i < system.length; ++i)
total += system[i] * solution[i];
return total;
}
In the above code system is array of coefficients c[i] and total is S. There is an obvious restriction: system should have no any zero elements (this lead to infinite number of solutions). A slight modification of the above code avoids this restriction.
Assuming you have class Product which exposes a property called Price, this is a way to do it:
public List<List<Product>> GetAffordableCombinations(double availableMoney, List<Product> availableProducts)
{
List<Product> sortedProducts = availableProducts.OrderByDescending(p => p.Price).ToList();
//we have to cycle through the list multiple times while keeping track of the current
//position in each subsequent cycle. we're using a list of integers to save these positions
List<int> layerPointer = new List<int>();
layerPointer.Add(0);
int currentLayer = 0;
List<List<Product>> affordableCombinations = new List<List<Product>>();
List<Product> tempList = new List<Product>();
//when we went through all product on the top layer, we're done
while (layerPointer[0] < sortedProducts.Count)
{
//take the product in the current position on the current layer
var currentProduct = sortedProducts[layerPointer[currentLayer]];
var currentSum = tempList.Sum(p => p.Price);
if ((currentSum + currentProduct.Price) <= availableMoney)
{
//if the sum doesn't exeed our maximum we add that prod to a temp list
tempList.Add(currentProduct);
//then we advance to the next layer
currentLayer++;
//if it doesn't exist, we create it and set the 'start product' on that layer
//to the current product of the current layer
if (currentLayer >= layerPointer.Count)
layerPointer.Add(layerPointer[currentLayer - 1]);
}
else
{
//if the sum would exeed our maximum we move to the next prod on the current layer
layerPointer[currentLayer]++;
if (layerPointer[currentLayer] >= sortedProducts.Count)
{
//if we've reached the end of the list on the current layer,
//there are no more cheaper products to add, and this cycle is complete
//so we add the list we have so far to the possible combinations
affordableCombinations.Add(tempList);
tempList = new List<Product>();
//move to the next product on the top layer
layerPointer[0]++;
currentLayer = 0;
//set the current products on each subsequent layer to the current of the top layer
for (int i = 1; i < layerPointer.Count; i++)
{
layerPointer[i] = layerPointer[0];
}
}
}
}
return affordableCombinations;
}

Categories