Related
I've come to a mathematical problem which for I can't program the logic.
Let me explain it with an example:
Let's say I have 4 holes and 3 marbles, the holes are in order and my marbles are A,B and C and also in order.
I need to get every posible ORDERED combination:
ABC4
AB3C
A2BC
1ABC
This is very simple, but what if the number of holes changes? Let's say now I have 5 holes.
ABC45
AB3C5
A2BC5
1ABC5
AB34C
A2B4C
1AB4C
A23BC
1A3BC
12ABC
Now let's say we have 5 holes and 4 marbles.
ABCD5
ABC4D
AB3CD
A2BCD
1ABCD
And this can be any number of holes and any number of marbles.
The number of combinations is given by:
$combinations = factorial($number_of_holes)/(factorial($number_of_marbles)*factorial($number_of_holes-$number_of_marbles)))
(Here it is the factorial function in case you need it)
function factorial($number) {
if ($number < 2) {
return 1;
} else {
return ($number * factorial($number-1));
}
}
What I need and can't figure out how to program, is a function or a loop or something, that returns an array with the position of the holes, given X numbers of holes and Y number of marbles.
For first example it would be: [[4],[3],[2],[1]], for second: [[4,5],[2,5],[1,5],[3,4],[2,4],[1,5],[2,3],[1,3],[1,2]], for third: [[5],[4],[3],[2],[1]].
It doesn't have to be returned in order, I just need all the elements.
As you can see, another approach is the complementary or inverse or don't know how to call it, but the solution is every combinations of X number of free holes given Y number of holes, so, If I have 10 holes, and 5 marbles, there would be 5 free holes, the array returned would be every combination of 5 that can be formed with (1,2,3,4,5,6,7,8,9,10), which are 252 combinations, and what I need is the 252 combinations.
Examples for the 2nd approach:
Given an array=[1,2,3,4], return every combination for sets of 2 and 3.
Sets of 2
[[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]
Sets of 3
[[1,2,3],[1,2,4],[1,3,4],[2,3,4]]
What I need is the logic to do this, I'm trying to do it in PHP, but I just can't figure out how to do it.
The function would receive the array and the set size and would return the array of sets:
function getCombinations($array,$setize){
//magic code which I can't figure out
return array(sets);
}
I hope this is clear enough and someone can help me, I've been stuck for several days now, but it seems to be just too much for me to handle by myself.
This post, PHP algorithm to generate all combinations of a specific size from a single set, is for all possible combinations, repeating the elements and order doesn't matter, its a good lead, I did read it, but it doesn't solve my problem, it's very different. I need them without repeating the elements and ordered as explained.
Let's say if I have already a set of [3,4] in my array, I don't want [4,3] as an other set.
Here's a recursive solution in PHP:
function getCombinations($array, $setsize){
if($setsize == 0)
return [[]];
// generate combinations including the first element by generating combinations for
// the remainder of the array with one less element and prepending the first element:
$sets = getCombinations(array_slice($array, 1), $setsize - 1);
foreach ($sets as &$combo) {
array_unshift($combo, $array[0]);
}
// generate combinations not including the first element and add them to the list:
if(count($array) > $setsize)
$sets = array_merge($sets, getCombinations(array_slice($array, 1), $setsize));
return $sets;
}
// test:
print_r(getCombinations([1, 2, 3, 4], 3));
Algorithm works like this:
If setsize is 0 then you return a single, empty combination
Otherwise, generate all combinations that include the first element, by recursively generating all combinations off the array excluding the first element with setsize - 1 elements, and then prepending the first element to each of them.
Then, if the array size is greater than setsize (meaning including the first element is not compulsory), generate all the combinations for the rest of the list and add them to the ones we generated in the second step.
So basically at each step you need to consider whether an element will be included or excluded in the combination, and merge together the set of combinations representing both choices.
I've been searching for a while to try and arrive at some sort of solution for a problem that is currently roadblocking a task I'm trying to complete.
I've come across a few solutions in other programming languages that I really can't understand despite my attempts at doing so. I've also seen a lot of terminology surrounding this problem such as permutations, refactoring, subset sums, coins in a dollar, etc.
If I'm going about this the wrong way, please do feel free to let me know.
Here's the problem in a nutshell:
Given a set (array) of numbers,
ex: 2, 3, 7, 14,
how could I find what combinations of those numbers add up to (or equal) a specific sum, ex: 14.
An example of some of the potential combinations for the above example numbers:
3 + 3 + 3 + 3 + 2
7 + 3 + 2 + 2
7 + 7
14
Since the problem I'm trying to solve is in PHP, I'd love if there were a solution that could be offered in that language. If not, even if someone could better explain what the problem is that I'm trying to solve, and potential methods of doing so, I'd be greatly appreciative.
Or again if I might be going about this the wrong way, I'm all ears.
To generate ALL solutions you are going to need to use some kind of backtracking, "guess" if the first number is in the solution or not, and recurse for each of the possibilities (it is needed to sum the result, or it is not).
Something like the following pseudo-code:
genResults(array, sum, currentResult):
if (sum == 0): //stop clause, found a series summing to to correct number
print currentResult
else if (sum < 0): //failing stop clause, passed the required number
return
else if (array.length == 0): //failing stop clause, exhausted the array
return
else:
//find all solutions reachable while using the first number (can use it multiple times)
currentResult.addLast(array[0])
genResults(array, sum - array[0], currentResult)
//clean up
currentResult.removeLast()
//find all solutions reachable while NOT using first number
genResults(array+1, sum, currentResult)
//in the above array+1 means the subarray starting from the 2nd element
Here's what I have managed to come up with thus far, based on amit's feedback and example, and some other examples.
So far it appears to be working - but I'm not 100% certain.
$totals = array();
$x=0;
function getAllCombinations($ind, $denom, $n, $vals=array()){
global $totals, $x;
if ($n == 0){
foreach ($vals as $key => $qty){
for(; $qty>0; $qty--){
$totals[$x][] = $denom[$key];
}
}
$x++;
return;
}
if ($ind == count($denom)) return;
$currdenom = $denom[$ind];
for ($i=0;$i<=($n/$currdenom);$i++){
$vals[$ind] = $i;
getAllCombinations($ind+1,$denom,$n-($i*$currdenom),$vals);
}
}
$array = array(3, 5, 7, 14);
$sum = 30;
getAllCombinations(0, $array, $sum);
var_dump($totals);
I have written a function to generate a random string of 7 alphanumeric characters which I am then inserting in a mysql database.
Here is the code :
function getRandomID(){
$tmp ="";
$characters=array("A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","1","2","3","4","5","6","7","8","9");
for($i=0;$i<7;$i++)
$tmp.=$characters[rand(0,count($characters)-1)];
return $tmp;
}
I am not checking for duplicates atm because I anticipate there will be no more than 1000 entries in the database and I've calculated that this function can return (35)^7 = 64,339,296,875 possible values.
I am testing it out locally as well as on a live server.
The problem is just in the last hour , this function generated duplicate values twice.
I came upon 3 entries in the database all of which had the same random string.
I do not know what could have caused this as I tried numerous times afterwards and the problem wasn't reproducible.
Does anybody have any idea what could be going on here?
Many thanks in advance
Designing your code with the mindset of "meh, that's not going to happen" is a very risky game, just do it properly once so you don't have to get back to your code multiple times to quick-fix minor things like these.
Do the duplicate check and you'll be solid.
You can create a function like
function stringExists($string)
{
...
return $boolValue;
}
And you can easily create a while loop that generates a new string while an old one has been generated.
$duplicate = true;
while($duplicate)
{
$newString = getRandomId();
$duplicate = !stringExists($string);
}
// Work with the newest string that is not a duplicate.
If you really want to get into it
You can then take a look at the documentation for rand if you want to find out what might be causing your problem. Besides, 3 entries doesn't mean anything if we don't know how many total entries there are. Also sometimes "random" function are not as random as one might think, sometimes random functions in some programming languages are always usable but require some sort of an initiation before they become "truly" random.
The time of the inserts might also be a part of the problem, there are plenty of threads on the internet, like this one on stackoverflow, that have some interesting points that can affect your "random"ness.
Whether it's true or not, not which has been pointed out in the comment, you can be pretty sure to find an answer to your question in related threads and topics.
Short answer: Don't think about it and do a duplicate check, it's easy.
Note that you should, of-course, make your ID be a UNIQUE constraint in the database to begin with.
Random != unique. Collisions happen. Check that the value is unique before you insert into the database, and/or put an integrity contstraint in your DB to enforce uniqueness.
If you're using a very old version of PHP [eg. pre-4.2] you have to seed the random number generator with srand().
Aside from #2, it's probably not your getRandomID() function but something else in your code that's re-using previous values.
If you need to enrer unique data in the DB, you may use PHP function uniqid(). (http://ca3.php.net/uniqid)
The function generates more-less random string based on current microseconds. So in theory it is unique.
But still, its always good to check before insert. Or at least put UNIQUE index on the field.
You could do something like this:
function randomString($length, $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") {
$string = "";
$charsLength = strlen($chars);
for ($i = 0; $i < intval($length); $i++) {
$string .= $chars[rand(0, $charsLength - 1)];
}
return $string;
}
The function above will generate a random string in the given length from the given characters. This makes it a little bit more flexible, than your implementation, if you need to use it in amother context later.
Then you could do a check like this:
$id = null;
do {
$id = randomString(7);
} while (!isUnique($id));
// do your insert here. You need to write your isUnique, so that it checks if
// the given string is unique or not.
Ok I am just learning about Big-O and someone gave me a conceptual question, to take as a means of trying to learn. However just barely starting out with Big-O I only know concept per say.
I've been told If I take an array of sorted INT values how would I write a function that essentially would return true (or false) if the sum of any two of the numbers in the array equal zero. So my assumption is I would have an array like.
array("0","1","2","3","4")
Just an example Im sure the array is much larger. What I am trying to figure out how is how can I really do that? I don't want to iterate over the array x times where x is the count of the array and then some to try every combination, thats just insane and if the array is large enough all Ill do is run out of memory and either bottle neck myself server side or client side depending on the route I run with it javascript or php.
So whats a good way of tackling that cause I sure have no decent clue at the moment.
Big O notation is about classifying how "intense" certain algorithms are.
To take your example of a poor algorithm to solve that problem, you have n elements of an array, and one way to see if any combination of elements sums to zero is for each 1 element, check all of the other n-1 elements for the zero sum.
Big O ignores the constants, so "for each element" = n you check n elements, and get O(n*n) or O(n^2).
Like you said, there's probably a better way. Since the array is sorted, try to think of some properties of that array, like big numbers at one end and small at the other.
That should help you think of an algorithm that has a lower complexity than O(n^2).
Since it's sorted, you can find the value closest to zero in O(log(n)) time through binary search. That value splits the array in two subarrays, one with values less than zero (call it A) and one with values greater than zero (call it B). Obvisouly, two values a and b sum up to zero if and only if a = -b. So you can do:
var i = 0;
var j = 0;
while (i < A.length && j < B.length) {
if (A[i] == -B[j]) {
return true;
} else if (A[i] < -B[j]) {
i++;
} else {
j++;
}
}
return false;
The complexity of the algorithm above is O(n/2)=O(n), and since O(n) > O(log(n)) (binary search), then the overall complexity is still O(n). Note then, that you could have iterated over the array instead of using binary search and the complexity would have been the same.
That actually makes me think you can do it all in a single loop. Just keep two pointers, one at the start of the array, one at the end, and move the progressively towards the middle. Complexity is is still O(n/2) = O(n):
var i = myArray.length-1;
var j = 0;
while (i > j) {
if (myArray[i] == -myArray[j]) {
return true;
} else if (myArray[i] > -myArray[j]) {
i--;
} else {
j++;
}
}
return false;
I always think to myself after solving a programming challenge that I have been tied up with for some time, "It works, thats good enough".
I don't think this is really the correct mindset, in my opinion and I think I should always be trying to code with the greatest performance.
Anyway, with this said, I just tried a ProjectEuler question. Specifically question #2.
How could I have improved this solution. I feel like its really verbose. Like I'm passing the previous number in recursion.
<?php
/* Each new term in the Fibonacci sequence is generated by adding the previous two
terms. By starting with 1 and 2, the first 10 terms will be:
1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...
Find the sum of all the even-valued terms in the sequence which do not exceed
four million.
*/
function fibonacci ( $number, $previous = 1 ) {
global $answer;
$fibonacci = $number + $previous;
if($fibonacci > 4000000) return;
if($fibonacci % 2 == 0) {
$answer = is_numeric($answer) ? $answer + $fibonacci : $fibonacci;
}
return fibonacci($fibonacci, $number);
}
fibonacci(1);
echo $answer;
?>
Note this isn't homework. I left school hundreds of years ago. I am just feeling bored and going through the Project Euler questions
I always think to myself after solving
a programming challenge that I have
been tied up with for some time, "It
works, thats good enough".
I don't think this is really the
correct mindset, in my opinion and I
think I should always be trying to
code with the greatest performance.
One of the classic things presented in Code Complete is that programmers, given a goal, can create an "optimum" computer program using one of many metrics, but its impossible to optimize for all of the parameters at once. Parameters such as
Code Readabilty
Understandability of Code Output
Length of Code (lines)
Speed of Code Execution (performance)
Speed of writing code
Feel free to optimize for any one of these parameters, but keep in mind that optimizing for all of them at the same time can be an exercise in frustration, or result in an overdesigned system.
You should ask yourself: what are your goals? What is "good enough" in this situation? If you're just learning and want to make things more optimized, by all means go for it, just be aware that a perfect program takes infinite time to build, and time is valuable in and of itself.
You can avoid the mod 2 section by doing the operation three times (every third element is even), so that it reads:
$fibonacci = 3*$number + 2*$previous;
and the new input to fibonacci is ($fibonnacci,2*$number+$previous)
I'm not familiar with php, so this is just general algorithm advice, I don't know if it's the right syntax. It's practically the same operation, it just substitutes a few multiplications for moduluses and additions.
Also, make sure that you start with $number as even and the $previous as the odd one that precedes it in the sequence (you could start with $number as 2, $previous as 1, and have the sum also start at 2).
Forget about Fibonacci (Problem 2), i say just advance in Euler. Don't waste time finding the optimal code for every question.
If your answer achieves the One minute rule then you are good to try the next one. After few problems, things will get harder and you will be optimizing the code while you write to achieve that goal
Others on here have said it as well "This is part of the problem with example questions vs real business problems"
The answer to that question is very difficult to answer for a number of reasons:
Language plays a huge role. Some languages are much more suited to some problems and so if you are faced with a mismatch you are going to find your solution "less than eloquent"
It depends on how much time you have to solve the problem, the more time to solve the problem the more likely it is you will come to a solution you like (though the reverse is occasionally true as well too much time makes you over think)
It depends on your level of satisfaction overall. I have worked on several projects where I thought parts where great and coded beautifully, and other parts where utter garbage, but they were outside of what I had time to address.
I guess the bottom line is if you think its a good solution, and your customer/purchaser/team/etc agree then its a good solution for the time. You might change your mind in the future but for now its a good solution.
Use the guideline that the code to solve the problem shouldn't take more than about a minute to execute. That's the most important thing for Euler problems, IMO.
Beyond that, just make sure it's readable - make sure that you can easily see how the code works. This way, you can more easily see how things worked if you ever get a problem like one of the Euler problems you solved, which in turn lets you solve that problem more quickly - because you already know how you should solve it.
You can set other criteria for yourself, but I think that's going above and beyond the intention of Euler problems - to me, the context of the problems seem far more suitable for focusing on efficiency and readability than anything else
I didn't actually test this ... but there was something i personally would have attempted to solve in this solution before calling it "done".
Avoiding globals as much as possible by implementing recursion with a sum argument
EDIT: Update according to nnythm's algorithm recommendation (cool!)
function fibonacci ( $number, $previous, $sum ) {
if($fibonacci > 4000000) { return $sum; }
else {
$fibonacci = 3*$number + 2*$previous;
return fibonacci($fibonnacci,2*$number+$previous,$sum+$fibonacci);
}
}
echo fibonacci(2,1,2);
[shrug]
A solution should be evaluated by the requirements. If all requirements are satisfied, then everything else is moxy. If all requirements are met, and you are personally dissatisfied with the solution, then perhaps the requirements need re-evaluation. That's about as far as you can take this meta-physical question, because we start getting into things like project management and business :S
Ahem, regarding your Euler-Project question, just my two-pence:
Consider refactoring to iterative, as opposed to recursive
Notice every third term in the series is even? No need to modulo once you are given your starting term
For example
public const ulong TermLimit = 4000000;
public static ulong CalculateSumOfEvenTermsTo (ulong termLimit)
{
// sum!
ulong sum = 0;
// initial conditions
ulong prevTerm = 1;
ulong currTerm = 1;
ulong swapTerm = 0;
// unroll first even term, [odd + odd = even]
swapTerm = currTerm + prevTerm;
prevTerm = currTerm;
currTerm = swapTerm;
// begin iterative sum,
for (; currTerm < termLimit;)
{
// we have ensured currTerm is even,
// and loop condition ensures it is
// less than limit
sum += currTerm;
// next odd term, [odd + even = odd]
swapTerm = currTerm + prevTerm;
prevTerm = currTerm;
currTerm = swapTerm;
// next odd term, [even + odd = odd]
swapTerm = currTerm + prevTerm;
prevTerm = currTerm;
currTerm = swapTerm;
// next even term, [odd + odd = even]
swapTerm = currTerm + prevTerm;
prevTerm = currTerm;
currTerm = swapTerm;
}
return sum;
}
So, perhaps more lines of code, but [practically] guaranteed to be faster. An iterative approach is not as "elegant", but saves recursive method calls and saves stack space. Second, unrolling term generation [that is, explicitly expanding a loop] reduces the number of times you would have had to perform modulus operation and test "is even" conditional. Expanding also reduces the number of times your end conditional [if current term is less than limit] is evaluated.
Is it "better", no, it's just "another" solution.
Apologies for the C#, not familiar with php, but I am sure you could translate it fairly well.
Hope this helps, :)
Cheers
It is completely your choice, whether you are happy with a solution or whether you want to improve it further. There are many project Euler problems where a brute force solution would take too long, and where you will have to look for a clever algorithm.
Problem 2 doesn't require any optimisation. Your solution is already more than fast enough.
Still let me explain what kind of optimisation is possible. Often it helps to do some research on the subject. E.g. the wiki page on Fibonacci numbers contains this formula
fib(n) = (phi^n - (1-phi)^n)/sqrt(5)
where phi is the golden ratio. I.e.
phi = (sqrt(5)+1)/2.
If you use that fib(n) is approximately phi^n/sqrt(5) then you can find the index of the largest Fibonacci number smaller than M by
n = floor(log(M * sqrt(5)) / log(phi)).
E.g. for M=4000000, we get n=33, hence fib(33) the largest Fibonacci number smaller than 4000000. It can be observed that fib(n) is even if n is a multiple of 3. Hence the sum of the even Fibonacci numbers is
fib(0) + fib(3) + fib(6) + ... + fib(3k)
To find a closed form we use the formula above from the wikipedia page and notice that
the sum is essentially just two geometric series. The math isn't completely trivial, but using these ideas it can be shown that
fib(0) + fib(3) + fib(6) + ... + fib(3k) = (fib(3k + 2) - 1) /2 .
Since fib(n) has size O(n), the straight forward solution has a complexity of O(n^2).
Using the closed formula above together with a fast method to evaluate Fibonacci numbers
has a complexity of O(n log(n)^(1+epsilon)). For small numbers either solution is of course fine.