Max Profit Algorithm - php

I came across this question on one of my interviews, can't remember the company.
A product marketer has several suppliers of a product.
The stock is represented by an array of quantities for each supplier.
As items are purchased, the supplier raises the price by 1 unit per item purchased, reducing the marketer's profit per unit.
Assume the marketer's profit on any one item is the same as the number of those items the supplier has.
For example, the supplier has 4 units, the marketer's profit on the first unit sold is 4, then 3, then 2, and the last one the profit is 1.
Given the array of quantities at suppliers and the number of items ordered, determine the highest profit that can be generated.
Example
inventory = [3, 5]
order = 6
Two suppliers have inventory = [3, 5] units to sell, and 6 items
were ordered.
The maximum profit is made by selling 1 for 5, 1 for 4, and 2 at 3 and 2 at 2 units profit. The two suppliers are
left with a unit of product each.
The maximum profit generated is 5 + 4 + 2 * 3 + 2 * 2 = 19
Complete the function maxProfit with the following parameter(s);
int inventory[n]: an array of integers representing units available in the stock per supplier
int order: a long integer, the number of items ordered
Returns:
long int: the maximum profit achievable
Constraints
1 ≤ n ≤ 10
1 ≤ inventory[i] ≤ 10^5, where 0 ≤ i < n
1 ≤ order ≤ sum(inventory)
My answer was like this
function maximumProfit($inventory, $order) {
// Write your code here
$profit = 0;
for ($i=0; $i<$order; $i++) {
$inventory = sortInventoryMax($inventory);
$profit += $inventory[0];
$inventory[0]--;
}
return $profit;
}
function sortInventoryMax($inventory) {
usort($inventory, function($a, $b) {
return $b - $a;
});
return $inventory;
}
it fails on the cases when it took more than 9 seconds time execution.

You need to use a max heap to keep track of running maximum in your array as sorting at every iteration is time consuming. PHP has the max heap class SplMaxHeap to do the job for you.
We put all the elements in the max heap. Now, for every order, extract the top value from the heap, add it to the profit, and insert a new unit reduced price in to the heap again.
Snippet:
<?php
function maximumProfit($inventory, $order) {
$profit = 0;
$max_heap = new SplMaxHeap();
foreach($inventory as $inv){
$max_heap->insert($inv);
}
while($order-- > 0){
$curr = $max_heap->extract();
$profit += $curr;
if($curr > 1) $max_heap->insert($curr - 1);
}
return $profit;
}
echo maximumProfit([3,5], 6);
Online Demo

Related

I need to sort a group of players into teams amd make them as "fair" as possible

I have a group of 9 players with values (skill levels) that I want to sort into 3 teams of 3, such that the total skill level of each team is as close to the same.
For the group, (1, 1, 3, 4, 4, 4, 4, 5, 10), the best arrangement would be (10, 1, 1), (5, 4, 3), (4, 4, 4), where all 3 teams have a total value of 12.
I grabbed a deck of cards & pulled out 9 cards with those values, sorted them from 10-1, and started dealing them out. If I just alternate the order of each round of dealing, i.e. first round is left to right, next right to left, etc.. I get (10, 4, 3), (5, 4, 1), (4, 4, 1) for totals of 17, 10, 9.
So I thought, what if I sort the teams after each round?
So I deal the first round, still distributing the players from highest to lowest, then sort ascending, then deal the next round until the cards are all out. This results in (10, 4, 1), (5, 4, 1), (4, 4, 3) for totals of 15, 10, 11. A little better, but not the best arrangement.
I can see that my problem is the 2 lowest scoring players have to get matched with the 1 highest scoring player, but am struggling to find an algorithm that will do that.
Here's my code in php
## Get the registered players
$players = array(1, 1, 3, 4, 4, 4, 4, 5, 10);
$total_players = count($players);
## Sort the array by skill level
rsort($players);
## Determine number of teams
$num_teams = ceil($total_players / 3);
## Distribute players on teams
$teams = array();
$team_num = 0;
foreach ($players as $player)
{
$teams[$team_num]["players"][] = $player;
$team_num++;
if ($team_num == $num_teams)
{
# reset counter to 0
$team_num = 0;
# Sort the teams on total score
foreach ($teams as $key => $team)
{
$total_skill = 0;
foreach ($team["players"] as $player)
{
$total_skill += $player;
}
$teams[$key]["total_skill"] = $total_skill;
}
aasort_asc($teams, "total_skill");
$teams = array_values($teams);
}
}
## Display teams with total scores
echo "<pre>"; print_r($teams); echo "</pre>";
There very well may be a solution posted already, but I failed to come up with the right search terms.
I suppose there may be a solution that has nothing to do with players & teams.
Any help or guidance would be most appreciated.
Be Well
I've tried several permutation algorithms. They both blow out memory after about 10 array elements.
Here's an algorithmic answer.
Assumptions:
1. Team size max ranges from min to min+1 (equal or one more only)
2. Skill levels always positive, arbitrarily large
Algorithm - initial/prep:
Determine team size min and max.
Determine number of teams based on team size min and max.
Total all skills into a single sum.
Divide total skills by team count to get average skill per team.
Divide total skills by # of contestants = average skill each.
Sort contestants by skill level.
Algorithm - picking teams:
Determine this team's size (min or max) however you like.
Take current team's total skill (empty=0 at first).
Add highest skill contestant to team.
Compare sum of skills in team to average for that many contestants.
If this is above average, add next contestant from low skill end.
If this is below average, add next from high skill end.
Continue 4-6 till team is filled.
Continue with further teams (1-7) till done.

Weighted Average Calculation Based on Different Criteria

I am working on a reverse auctioning system in PHP. My customers get bids from different vendors against their requested products/services. While displaying received bids to the logged in customer, I need to do a sorting based on weighted average against following criteria:
Price (40%) -> Lowest price is good.
Vendor Rating (20%) -> Rating = 1-5 (5 being best).
Delivery Options (10%) -> More delivery options offered the best.
Distance (10%) -> Lowest distance from customer location is the best.
Payment Methods (10%) -> More payment methods offered the best.
So far I have been able to create following formula:
$weightage = ($price*.40) + ($rating*.20) + ($delivery_options*.10) + ($distance*.10) + ($payment_methods*.10);
I need to show bids having highest weightage value on top. I am confused about the addition/subtraction of the weightage based on what is best for customer i.e if price is lower then this should be considered best for customer and should I add weightage or subtract weightage?
Any help would be appreciated. Thanks
I got it sorted out. Here's what I did:
Quote #1 (Best Quote)
Price: 120
Rating: 5
Delivery Options: 2
Distance: 2000 km
Payment Methods: 5
Availability: 1
Price Factor = 1 - ( (Price - Min Price) / (Max Price - Min Price) ) = 1
Rating Factor = ( (Rating - Min Rating) / (Max Rating - Min Rating) ) = 1
Delivery Options Factor = ( (Delivery Options - Min Delivery Options) / (Max Delivery Options - Min Delivery Options) ) = 1
Distance Factor = 1 - ( (Distance - Min Distance) / (Max Distance - Min Distance) ) = 1
Payment Methods Factor = ( (Payment Methods - Min Payment Methods) / (Max Payment Methods - Min Payment Methods) ) = 1
Availability Factor = ( (Availability - Min Availability) / (Max Availability - Min Availability) ) = 1
Quote #1 Weightage = (Price Factor * 40%) + (Rating Factor * 20%) + (Delivery Options Factor * 10%) + (Distance Factor * 10%) + (Payment Methods Factor * 10%) + (Availability Factor * 10%) = 1
Apply same formula to other quotes, you'll get weightage for each quote, and then you can sort quotes easily (Order by Quote Weightage Asc/Desc).

mysql query order by two column with weight for each of them

I have table Foo(id, name, rateAvg, rateNum). rateAvg is between 1 to 5 and rateNum is number of rates by users.
I query table with mysql and order them by most rated Foos like this:
SELECT * FROM Fooo ORDER BY rateAVG DESC, rateNum DESC
but that is not fair enough, for example one row has rateAvg of 4.8 with 1000 rates and the other with rateAvg of 5 and 10 rates and by my query item two come first.
Edit:
by comment of #kingkero I found out that each of rateAvg and rateNum should have some weight for ordering, how can apply that in my query
You can try to apply a bayesian average, but you should pre calculate this rating and store in one of your fields.
b(r) = [ W(a) * a + W(r) * r ] / (W(a) + W(r)]
r = average rating for an item
W(r) = weight of that rating, which is the number of ratings
a = average rating for your collection
W(a) = weight of that average, which is an arbitrary number,
but should be higher if you generally expect to have more ratings
for your items; 100 is used here, for a database which expects
many ratings per item
b(r) = new bayesian rating
For example in your case:
a = (4.8 * 1000 + 5 * 10) / 1010 = 4.8019
r1 = 4.8
r2 = 5
W(a) = 100 // arbitrary average weight
W(r1) = 1000 // weight of first raiting
W(r2) = 10 // weight of second rating
b(r1) = (100 * 4.8019 + 1000 * 4.8) / (100 + 1000) = 4.8001
b(r2) = (100 * 4.8019 + 10 * 5) / (100 + 10) = 4.8199
So you can see that these values are close to each other and you can try to change average weight to setup this bayesian average rating for your case.

split data according to N number of categories with X number of total data limit

OK. I am working in a PHP project.
and I have some categorized data in my database -
For example- I have data of Three Categories.
Category 1
Category 2
Category 3
Categories could be changed. Like 2,3,4,5 etc.
And i have facility to show a limit of data.
Ex. Lets say Admin want to show only 9 Records
So now we have three categories and x limit (which is 9 according to example). and best thing is that-
I need to show N numbers of records from each category and total X records.
Here N numbers is for each Category. So it will look like this:
N1 + N2 + N3 = X (limit ex. 9)
N1 from Category1 N2 from Category2 N3 from Category3
So basically i have formulated it like this -
NumberOfRecordPerCategory = X (limit) / N (Number of Categories)
But it is actually working if i set the limit multiple of 3 like 6,9,12 etc.
So my Question is-
How do i manage the total records according to limit (X) -
1. if Admin set the limit which is not multiple by 3 like 8,10,11, etc.
2. if Admin change the number of category Like 2 or 4, then how could i manage this?
Please give some idea about this. and please let me know if Question is not clear.
What I would choose for your requirement is ceil (round fractions up). If there is some fractions because the number is not multiple by 3, for example 11 with 3 categories, then have the limitation of 4,4,3 for each category. This could be done by having two limit values: limit of total, limit per category.
Here's my suggestion for your needs (code) :
<?php
/*
$data = array (
"category1" => array (1,2,3,4,5),
"category2" => array (10,20,30,40,50),
"category3" => array (100,200,300,400,500),
);
*/
$limit = 11; // given by the admin
$number_of_categories = count($data);
$limit_per_category = ceil ( $limit / $number_of_categories ); // ceil (11 / 3) = 4
$cnt = 0; // tracking the total number of retrieved data.
foreach ($data as $row) {
$cnt_category = 0;
foreach ($row as $item) {
echo $item . "\n"; // print the first N (NumberOfRecordPerCategory) data
$cnt_category++;
$cnt++;
if ($cnt >= $limit) break; // exit the loop
if ($cnt_category >= $limit_per_category) break; // move to next category
}
if ($cnt >= $limit) break; // exit the loop
}

mysql union or join just one query

I need to find a better way to get the discount for each article price in our web shop depending on which pricelist or pricelist/discount list a customer has. I think this is possible to do in just one query instead of the 5 I have today, but I really do not know where to start.
All our customers have a pricelist and some have both pricelist and one extra discount list. Today we have about 25 different pricelists and about 100 extra discount lists.
All the pricelists are structured in the same manner; they have a price group and a discount in percent.
For example pricelist 01 could look like
A 20
B 35
C 20
The extra discount list is structured in a different manner and can have a fixed price or percentage. It also has three different priority levels: discount based on the article code (has priority 1), based on category (has priority 2) and based on price group (has priority 3).
Discount list 0013 could look like:
In the article tab
PL-2344-444 40 (%)
P-0455-23 200 (SEK)
In the category tab
C12 50 (%)
N12 35 (%)
Today I have three different queries to see if I get a hit in the discount list:
First I check to see if I get a hit in priority 1: (FIXED returns f and PERCENTAGE r)
SELECT DISCOUNT, FIXED, PERCENTAGE FROM PUPRIREG
WHERE ARTCODE = 'JO-23455' AND DISCOUNTLIST = '0013'
If the above returns 0, I do the second query, priority 2:
SELECT DISCOUNT, FIXED, PERCENTAGE FROM PUPRIREG
WHERE CATEGORY = 'C15' AND DISCOUNTLIST = '0013'
And the last one priority 3:
SELECT DISCOUNT, FIXED, PERCENTAGE FROM PUPRIREG
WHERE PRICEGROUP = 'F' AND DISCOUNTLIST = '0013'
If none of the extra discount lists returns 0 I get the discount from the pricelist
SELECT DISCOUNT FROM PUPRIREG WHERE PRICELIST = '01' AND PRICEGROUP = 'F'
I call the function like follows
$discount = discount($articlecode, $category, $pricegroup);
function discount($articlecode, $category, $pricegroup){
$articlecode = sanitizingData($articlecode);
$category = sanitizingData($category);
$pricegroup = sanitizingData($pricegroup);
// do priority 1
// prio 2
// prio 3
// pricelist
return $discount;
}
I would be so happy if someone could show me how to do this. I am using mysqli and php.
Many thanks
Best regards linda
You can do the queries with a union:
(SELECT DISCOUNT, FIXED, PERCENTAGE, 1 priority FROM PUPRIREG
WHERE ARTCODE = 'JO-23455' AND DISCOUNTLIST = '0013')
union
(SELECT DISCOUNT, FIXED, PERCENTAGE, 2 priority FROM PUPRIREG
WHERE CATEGORY = 'C15' AND DISCOUNTLIST = '0013')
union
(SELECT DISCOUNT, FIXED, PERCENTAGE, 3 priority FROM PUPRIREG
WHERE PRICEGROUP = 'F' AND DISCOUNTLIST = '0013')
union
(SELECT DISCOUNT, 0 fixed, 0 percentage, 4 priority FROM PUPRIREG
WHERE PRICELIST = '01' AND PRICEGROUP = 'F')
order by priority;
The additional artificial priority column and order by ensures, that you get the discounts properly sorted.

Categories