Finding the nearest array set - php

I wanted to know what possible algorithm I could use to find which in the set of array below is nearest to [0,0,0]. I am thinking of giving points to each set by adding the values of each set, but the problem is the array index 1 [0,2,1] will have a sum of 3 which is equal to array index 3. The answer below should be index 3, or do you have better suggestion? thanks in advance.
$sets = [
[4,5,6], // 0
[0,2,1], // 1
[1,3,0], // 2
[1,1,1], // 3
[0,1,3], // 4
[5,4,3], // 5
]

Well, what you're basically describing corresponds to finding the distance to the origin for a point in 3D space, the formula for which is:
Based on that, the point [1, 1, 1] is indeed closer to the origin than [0, 2, 1]:
In PHP, you could calculate the distances as follows:
$sets = [[4,5,6], [0,2,1], [1,3,0], [1,1,1], [0,1,3], [5,4,3]];
$distances = array_map(function ($i) {
return sqrt($i[0]**2 + $i[1]**2 + $i[2]**2);
}, $sets);
print_r($distances);
Finding the closest point then becomes trivial.

Related

Permutations and big arrays in PHP - performance issues

I have an array of numbers (int or float) and I need to find a value by combining array values. Once the smallest possible combination is found the function returns the array values. Therefore I start with sample-size=1 and keep incrementing it.
Here's a simplified example of the given data:
$values = [10, 20, 30, 40, 50];
$lookingFor = 80;
Valid outcomes:
[30, 50] // return this
[10, 20, 50], [10, 30, 40] // just to demonstrate the possible combinations
Permutations solve this problem and I've tried many different implementations (for example: Permutations - all possible sets of numbers, Get all permutations of a PHP array?, https://github.com/drupol/phpermutations). My favourite is this one with a parameter for permutation-size using the Generator pattern: https://stackoverflow.com/a/43307800
What's my problem? Performance! My arrays have 5 - 150 numbers and sometimes the sum of 30 array numbers is needed to find the searched value. Sometimes the value can't be found, which means I needed to try all possible combinations. Basically with permutation-size > 5 the task becomes too time consuming.
An alternative, yet not precise way is to sort the array, take the first X and last X numbers and compare with the searched value. Like this:
sort($values, SORT_NUMERIC);
$countValues = count($values);
if ($sampleSize > $countValues)
{
$sampleSize = $countValues;
}
$minValues = array_slice($values, 0, $sampleSize);
$maxValues = array_slice($values, $countValues - $sampleSize, $sampleSize);
$possibleMin = array_sum($minValues);
$possibleMax = array_sum($maxValues);
if ($possibleMin === $lookingFor)
{
return $minValues;
}
if ($possibleMax === $lookingFor)
{
return $maxValues;
}
return [];
Hopefully somebody has dealt with a similar problem and can guide me in the right direction. Thank you!
you must use combination instead of permutations {ex: P(15) = 130767436800 vs C(15) = 32768}
if array_sum < target_number then no solution exists
if in_array(target_number, numbers) solution found with 1 element
sort lowest to highest
start with C(n,2) where 2 represents 1st 2nd then 1st 3rd etc (static one is 1st element)
if above loop found no solution continue with 2nd 3rd then 2nd 4th, etc)
if C(n,2) had no solution then jump to C(n,3)s but this time 2 static numbers and 1 dynamic one
if loop ended with no solution then there exists no solution
lastly, I would adjust this question and ask in statistics branch of stack exchange (crossvalidated) since mean, median and cumulative distribution of the sums of the numbers may hint to decrease the number of iterations significantly and this is their profession.

Find the smallest positive integer that does not occur in an array

I am trying out the following codility.com exercise to improve my skills online, I was presented with the following problem.
This is a demo task.
Write a function:
class Solution { public int solution(int[] A); }
that, given an array A of N integers, returns the smallest positive
integer (greater than 0) that does not occur in A.
For example,
given A = [1, 3, 6, 4, 1, 2], the function should return 5.
Given A = [1, 2, 3], the function should return 4.
Given A = [-1, -3], the function should return 1.
Write an efficient algorithm for the following assumptions:
• N is an
integer within the range [1..100,000);
• each element of array A is an
integer within the range (-1,000,000..1,000,000).
Copyright 2009– by Codility Limited
rendered description
I solved it using the following solution:
<?php
class Solution {
public function($A) {
$posInts = [1, 2, 3, 4, 5, 6, 7, 8, 9];
$diffs = array_diff($postInts, $A);
$smallestPosInt = min($diffs);
return $smallestPosInt;
}
}
However upon submitting I got the following score:
Now I am very unsure of what I did wrong here or how I can rewrite the code with a better algorithm.
Check out this answer using Javascript in a way that works with the best possible performance -If I am not mistaken- O(N).
function solution(A) {
const set = new Set(A)
let i = 1
while (set.has(i)) {
i++
}
return i
}
I would just loop over (increment) any possible integers:
function solution($A) {
$result = 1;
$maxNumber = max($A);
for (; $result <= $maxNumber; $result++) {
if (!in_array($result, $A)) {
break;
}
}
return $result;
}
var_dump(solution([1, 3, 6, 4, 1, 2])); // int(5)
var_dump(solution([1, 2, 3])); // int(4)
var_dump(solution([-1, -3])); // int(1)
// As a bonus, this also works for larger numbers:
var_dump(solution([1, 3, 6, 4, 1, 2, 7, 8, 9, 10, 11, 12, 13, 5, 15])); // int(14)
Edit regarding performance:
As pointed out in the comments (and you already said yourself), this is not a very efficient solution.
While I do not have enough time on my hands currently to do real performance testing, I think this should be close to an O(n) solution: (keeping in mind that I am not sure how arrays are implemented on the C-side of PHP)
function solution($A) {
$result = 1;
$maxNumber = max($A);
$values = array_flip($A);
for (; $result <= $maxNumber; $result++) {
if (!isset($values[$result])) {
break;
}
}
return $result;
}
// Not posting the output again because it is naturally the same ;)
The "trick" here is to flip the array first so that the values become the indexes. Since a) we do not care about the original indexes and b) we do not care if duplicated values overwrite each other, we can safely do that.
Using isset() instead of in_array() should be a lot quicker since it basically just checks if a variable (in this case stored at a specific index of the array) exists and PHP does therefore not have to iterate through the array in order to check whether or not each number we loop over exists within it.
P.S.: After thinking twice I think this may still be closer to O(n*2) because max() probably loops to find the highest value. You could also remove that line and just check against the highest number there is in PHP as an emergency exit, like so: for (; $result <= PHP_INT_MAX; $result++) { ... } as a further optimization. Or maybe just hard-code the highest allowed number as specified in the task.
If we're allowed to modify the input, perform this in place, otherwise create a new array of size n + 1:
For each element encountered in the original array, if it is greater than n + 1 or smaller than 1, assign 0 at the element's index (index - 1 if performing in place); otherwise assign 1 at the index of the array the value is and assign 0 at its own index if it is different. After that run a second traversal and report the first index (index + 1 if performing in place) greater than zero with value 0, or n + 1.
[1, 3, 6, 4, 1, 2]
=>
[1, 1, 1, 1, 0, 1]
report 5

Algorithm that generates all versions of an array with possible weights assigned 0-100

I have a following array (php):
[
[id=>1,weight=]
[id=>2,weight=]
[id=>3,weight=]
[id=>4,weight=]
]
I need to create all possible versions of this array asigning 0-100 weight to each item['weight'] with a step of N.
I don't know how this type of problems are called. It is NOT permutation/combination.
Lets say N is 10, I am aiming to get:
[
[
[id=>1,weight=10]
[id=>2,weight=10]
[id=>3,weight=10]
[id=>4,weight=70]
]
[
[id=>1,weight=10]
[id=>2,weight=10]
[id=>3,weight=20]
[id=>4,weight=60]
]
[
[id=>1,weight=10]
[id=>2,weight=10]
[id=>3,weight=30]
[id=>4,weight=50]
]
[
[id=>1,weight=10]
[id=>2,weight=10]
[id=>3,weight=40]
[id=>4,weight=40]
]
...all possible combination of weights for id=x.
[
[id=>1,weight=70]
[id=>2,weight=10]
[id=>3,weight=10]
[id=>4,weight=10]
]
]
Sum of 4 item['weights'] in array on same level is always 100 (or 0.1). And inside parent array I've all possible combinations of weights from 10-100 for id=x.
This problem is sometimes described as allocating identical balls into distinct bins. You didn't specify your problem exactly, so I'll take a guess here but the logic will be identical.
I'll assume you're distributing b = N/step balls into 4 bins.
Think of the balls all in a row, and then using 3 bars to separate the balls into 4 bins:
*|||*****.
If N=10 and you're distributing 100 points, the above example is the same is 30, 20, 0, 50. If zeroes aren't allowed, you can reduce the amount you're distributing by 4*b and assume each bin starts out with N/step in it (so you're distributing the leftover points).
The number of ways to do this is choose(balls + bins - 1, bins - 1).
Theres probably a better way, but heres my attempt:
$result=array(); // Empty array for your result
$array=range(1117,7777); // Make an array with every number between 1117 and 7777
foreach ($array as $k=>$v) { // Loop through numbers
if ((preg_match('/[890]/',$v) === 0) && (array_sum(str_split($v, 1)) === 10)) {
// If number does not contain 8,9 or 0 and sum of all 4 numbers is 10
// Apply function to multiply each number by 10 and add to result array
$result[] = array_map("magnitude", str_split($v, 1));
}
}
function magnitude($val) { // function to multiply by 10 for array map
return($val * 10);
}
print_r($result);
Working demo here
EDIT
Sorry I realised my code explanation isn't totally clear and I condensed it all a bit too much to make it easy to follow.
In your example the first array would contain (10,10,10,70). For the sake of simplicity I divided everything by 10 for the calculations and then just multiplied by 10 once I had a result, so your array of (10,10,10,70) becomes (1,1,1,7). Then your final array would be (70,10,10,10) which would become (7,1,1,1).
My approach was to first to create an array containing every combination of these four numbers, which I did in two steps.
This line $array=range(1117,7777); creates an array like this (1117, 1118, 1119 ... 7775, 7776, 7777) (My number range should really have been 1117 - 7111 instead of 1117-7777).
Applying str_split($v, 1) to each value in the loop splits each 4 digit number in the array into another array conatining 4 single digit numbers, so 1117 will become (1, 1, 1, 7) etc
As each of your items can't have a weight below 10 or above 70 we use (preg_match('/[890]/',$v) === 0) to skip any arrays which have 0,8 or 9 in them anywhere, then array_sum(str_split($v, 1)) === 10) adds up the four digits in the array and only returns arrays which total 10 (you wanted ones which total 100, but I divided by 10 earlier).
array_map applies a function to each element in an array. In my example the function multiplies each value by 10, to undo the fact I divided by 10 earlier.
When you say is it possible to alter steps, can you give me a couple of examples of other values and the output you want for them?
If you want a totally different approach and using mysql isn't a problem then this also works:
Create a new table with a single row. Insert all the values you need to check
INSERT INTO `numbers` (`number`) VALUES
(10),
(20),
(30),
(40),
(50),
(60),
(70);
Then your php looks like this
$result=array();
try {
$dbh = new PDO('mysql:host=aaaaa;dbname=bbb', 'ccc', 'dddd');
foreach($dbh->query('SELECT *
FROM numbers a
CROSS JOIN // A cross join returns the cartesian product of rows
numbers b // so every row with every combination of the other rows
CROSS JOIN
numbers c
CROSS JOIN
numbers d
ON
a.number = b.number OR a.number != b.number') as $row) {
if (($row[0] + $row[1] + $row[2] + $row[3]) === 100) {
$result[] = $row;
}
}
$dbh = null;
} catch (PDOException $e) {
print "Error!: " . $e->getMessage() . "<br/>";
die();
}
print_r($result);

Find all combinations of x numbers where they sum to Y

I'm trying to write this solution in PHP.
Inputs:
number of numbers = x
smallest number = 1
sum of numbers = y
I'm not dealing with very large numbers, largest x is approximatly 50, largest y is approximatly 80.
rules: Within each set of numbers, the number proceeding the previous must be equal to or greater.
For example
x = 3
min = 1
y = 6
solution:
(1,1,4),(1,2,3)
note that (3,2,1) isn't a solution as they are in descending order.
This is easily solved via recursion. The time complexity though will be high. For a better (but slightly more complex solution) use dynamic programming.
Here's the idea:
If the size of the set is 1 then the only possible solution is the desired sum.
If the set is larger than one then you can merge a number X between the minimum and the desired sum with a set of numbers which add up to the desired sum minus X.
function tuplesThatSumUpTo($desiredSum, $minimumNumber, $setSize) {
$tuples = [];
if ($setSize <= 1) {
return [ [ $desiredSum ] ]; //A set of sets of size 1 e.g. a set of the desired sum
}
for ($i = $minimumNumber;$i < $desiredSum;$i++) {
$partial = tuplesThatSumUpTo($desiredSum-$i, $minimumNumber,$setSize-1);
$tuples = array_merge($tuples, array_map(function ($tuple) use ($i) {
$res = array_merge([$i], $tuple);
sort($res);
return $res;
},$partial));
}
return array_unique($tuples,SORT_REGULAR);
}
See it run:
http://sandbox.onlinephpfunctions.com/code/1b0e507f8c2fcf06f4598005bf87ee98ad2505b3
The dynamic programming approach would have you instead hold an array of sets with partial sums and refer back to it to fill in what you need later on.

How to filter out numbers in an array PHP

I don't know why I can't get my head around this problem. Probably because I should be getting rest, but I have to at least solve this first.
Lets say that I let people rent my bicycles.
I have 4 of them.
Each of them are numbered 1, 2, 3 & 4.
Today no. 1 & 3 was already rented(which is saved in the database)
The third customer arrives and I wanted to see the "available"
numbers, which are supposed to be 2 & 4.
$bicycles = array(1, 2, 3, 4);
$rent = array(1,3);
$available = array();
How to save the available numbers in $available and at the same time count the number of available bicycles?
The problem actually has more factors involved, but I'll be okay if I get to settle this first. Hopefully.
array_diff is a better way to go, but this is easier to follow.
// Your bikes
$Bikes = array(1, 2, 3, 4);
// Rented bikes
$Rented = array(2, 3);
// Create a result array that we will fill as we loop
$Available = array();
// Find the difference manually by looping
foreach($Bikes as $Bike){
// If the bike isn't rented, add it to the Available array
if(!in_array($Bike, $Rented)) array_push($Available, $Bike);
}
var_dump($Available); // should contain 1 and 4
You can use the function array_diff() to return the difference between two arrays, and count() or sizeof() to return the number of elements in an array.

Categories