I'm trying to make a genetic calculator. I have the following code:
<?php
$gene1 = 'BA';
$geneA = array();
$i = 0;
while ($i < strlen($gene1)) $geneA[] = substr($gene1,$i++,2);
$gene2 = 'MSBA';
$geneB = array();
$i = 0;
while ($i < strlen($gene2)) $geneB[] = substr($gene2,$i++,2);
$possabilities = array();
foreach ($geneA as $A) {
foreach ($geneB as $B) {
if ($A === strtoupper($A)) $possabilities[] = $A.$B;
else {
if ($B === strtoupper($B)) $possabilities[] = $B.$A;
else $possabilities[] = $A.$B;
}
}
}
print_r($possabilities);
?>
Which works to a degree, it pairs the genes in the array, however it isn't working properly. This pairing should just return BABA and MSBA. Instead it returns this:
Array ( [0] => BAMS [1] => BASB [2] => BABA [3] => BAA [4] => AMS [5] => ASB [6] => ABA [7] => AA )
Which isn't exactly ideal for my project. I thought a better idea would be to comma separate the genes like this $gene1 = 'BA'; and $gene2 = 'MS,BA'; and run a loop combining each gene that way, but i am unsure on how to do this properly. Can anyone shed some light on the idea at all?
I hope I'm right in assuming that
Genes are always made up of two pairs (MS, but not "M" and "S")
Each member of $geneA should be matched with each member of $geneB
Part 1: Resolving the error
In this case your algorithm for splitting has a serious flaw: It always progresses just for one step in the original string ($gene1 and $gene2)
function getGeneArray($geneString) {
// check the argument for fitting your needs!
if ( strlen($geneString) % 2 == 1 ) {
die('Supplied geneString is not made of pairs!'); // better not die - handle errors according to your application methodology
}
// add additional error-catching (there are only certain possible base-pairs, if something else is found you should reject the string
$genes = array();
$i = 0;
while ( $i < strlen($geneString) )
{
$genes[] = substr($geneString, $i, 2);
$i += 2; // Here is your mistake, you just $i++
}
return $genes;
}
With this little function you a) reduce duplicates in your code and b) get a determined outcome (no wrong genes)
Part 2: Making code document itself
Looking at your code it becomes clear, that uppercase-gene-pairs must come befor lowercase pairs, I try to communicate that with the code by using an extra function with a clear name.
function combinePairs($A, $B) {
// uppercase genes build the string first, which means B must be uppercase to come first and A cant be uppercase
if (strtoupper($A) !== $A && strotoupper($B) === $B) {
return $B.$A;
}
return $A.$B;
}
Part 3: Plugging it together
$geneA = getGeneArray($gene1);
$geneB = getGeneArray($gene2);
$possibilities = array();
foreach ($geneA as $A) {
foreach ($geneB as $B) {
$possibilities[] = combinePairs($A, $B);
}
}
print_r($possibilities);
Final Note
As a programmer you want to cater the needs of your client or input source, so of course you can split your genes with commas. Try to use the format most usable for your application and client input. In this case you could easily optain an array by using explode() (explode in the manual)
Related
I have this code:
public function get_names($number){
$names = array(
'None',
'Anton',
'bertha',
'Cesa',
'Dori',
'Egon',
'Frank',
'Gollum',
'Hans',
'Kane',
'Linus',
'Moose',
'Nandor',
'Oliver',
'Paul',
'Reese');
$bin = strrev(decbin($number));
$len = strlen($bin);
$output = array();
foreach(str_split($bin) as $key=>$char)
{
if ($key == sizeof($names)){
break;
}
if($char == 1)
{
array_push($output,$names[$key]);
}
}
return $output;
}
When I now call the function with number - let's say - 32256 I would get an array with "Kane, Linus, Moose, Nandor, Oliver, Paul".
Can anyone tell me what I would have to do when I want to give a certain names array and wanna get the number as a result where the certain bits are included? So exactly the other way around.
I found that code somewhere which works fine. But I need it vice versa.
Thanks in advance!
Andreas
EDIT: I want to know the decimal number when I e.g. have an array with "Anton, bertha,Cesa". I want to store those in a database instead of storing arrays with names each time. And when I need the names I just take the decimal number from database and use my function to get my name arrays.
If you just take the position in your $names array as being the bit position, you raise 2 to this position to give it the correct bit position and keep a running total of the items found...
$input = ["Cesa", "Gollum", "Hans"];
$output = 0;
foreach ( $input as $digit ) {
$output += pow(2,array_search($digit, $names ));
}
echo $output; // 392
with
$input = ["Kane", "Linus", "Moose", "Nandor", "Oliver", "Paul"];
gives
32256
Or as Barmar points out in his comment, you can save the lookup by inverting the names array using array_flip() which will mean that looking up each key will give the position in the array...
$output = 0;
$names = array_flip($names);
foreach ( $input as $digit ) {
$output += pow(2,$names[$digit]);
}
echo $output;
I am working on a genetics calculator which sort of works, however not as I intend it to haha.
I am using the following code:
<?php
function getGeneArray($geneString) {
// check the argument for fitting your needs!
if ( strlen($geneString) % 2 == 1 ) {
die('Supplied geneString is not made of pairs!'); // better not die - handle errors according to your application methodology
}
// add additional error-catching (there are only certain possible base-pairs, if something else is found you should reject the string
$genes = array();
$i = 0;
while ( $i < strlen($geneString) )
{
$genes[] = substr($geneString, $i, 2);
$i += 2; // Here is your mistake, you just $i++
}
return $genes;
}
function combinePairs($A, $B) {
// uppercase genes build the string first, which means B must be uppercase to come first and A cant be uppercase -- no longer important
if ($A !== $A && $B === $B) {
return $B.$A;
}
return $A.$B;
}
$gene1 = 'bbEe';
$gene2 = 'bbEe';
$geneA = getGeneArray($gene1);
$geneB = getGeneArray($gene2);
$possibilities = array();
foreach ($geneA as $A) {
foreach ($geneB as $B) {
$possibilities[] = combinePairs($A, $B);
}
}
The output from running the above code is:
Array ( [0] => bbbb [1] => bbEe [2] => Eebb [3] => EeEe )
When in reality I would like it to be something like:
Array ( [0] => bbee [1] => bbEe [2] => bbEE )
What can I do/change to get the right sort of outcome?
the first function can be simplified somewhat using str_split
function getGeneArray( $str ) {
if ( strlen( $str ) % 2 == 1 ) die('Supplied gene string is not made of pairs!');
return str_split( $str, 2 );
}
As for the combinePairs function it appears that it will only ever return $A.$B - without knowing HOW the calculations are supposed to work it is hard to give an answer.
I don't know if this is how the combinePairs function was supposed to be
function combinePairs( $A, $B ) {
return strtolower( $A )!=$A && $B===$B ? $B.$A : $A.$B;
}
Of possible interest in regards to calculating the sequences ( based upon your comments of "uppercase genes build the string first" then the ctype_* family of functions might be useful.
`ctype_lower( $str )` ~ tests if ALL characters in $str are lowercase.
`ctype_upper( $str )` ~ tests if ALL characters in $str are uppercase.
`ctype_alpha( $str )` ~ tests that all characters in $str are Alphabetic.
And to test if a string contains an Uppercase character you can do:
if( strtolower( $str ) != $str ){/* contains an uppercase character */}
There is an array with names, for example:
$donalds_nephews = array('Huey', 'Dewey', 'Louie');
array
(
[0] => Huey
[1] => Dewey
[2] => Louie
)
I want to shuffle this array, but ensure that no value of the original array has the same key as the shuffled one.
$donalds_nephews_shuffled = shuffle($donalds_nephews);
This could result in 6 possible permutations:
Huey, Dewey, Louie
Huey, Louie, Dewey
Dewey, Louie, Huey
Dewey, Huey, Louie
Louie, Dewey, Huey
Louie, Huey, Dewey
1st, 2nd, 4th and 5th must not be the result.
What's the best way to do so? It's for Secret Santa.
Shuffle the original array, then make a copy of it and shift all entries one over, then match the two back together to get your matches.
It's for Secret Santa.
Then start with a better algorithm. Currently you seem to be inferring that the key is a present giver and the value a present receiver (or vice versa). This then entails the additional check (and possible re-iteration of the shuffling) to ensure that nobody ends up giving a present to themselves.
But what if you just consider it as an ordered list of names, such that each entry gives to the next person on the list:
$victims=array('Huey', 'Dewey', 'Louie');
shuffle($victims);
$giver='';
foreach($victims as $receiver) {
if ($giver) print "$giver gives to $receiver\n";
$giver=$receiver;
}
$receiver=array_shift($victims);
print "$giver gives to $receiver\n";
just cause i need this for my secret santa :)
<?php
function compareArrays($original, $shuffled){
for($i = 0; $i < count($original); $i++ ){
if($original[$i] == $shuffled[$i]){
return false;
}
}
return true;
}
$donalds_nephews = array('Huey', 'Dewey', 'Louie','Igor','Stephan');
//avoid loops
for($j = 0; $j < 50; $j++){
$shuffled = $donalds_nephews;
shuffle($shuffled);
$good = compareArrays($donalds_nephews, $shuffled);
if($good) break;
}
if($good){
echo "here we go!\n";
foreach($shuffled as $k => $v){
echo "$k => $v \n";
}
}
else {
echo "try again \n";
}
?>
Don't make this complicated by trying to put all of this into one function.
Here's your pseudocode:
$givers = array( 'Huey', 'Dewey', 'Louie' );
$getters = $givers;
foreach ( $givers as $giver ) {
do {
pick a random $getter from $getters;
} until $getter <> $giver;
delete $getter from $getters;
print "$giver gives to $getter\n";
}
It is an old question, but you asked for the best way, so how about this?
function santaYates($array) {
$returnArray = array_values($array); // Cause we need a clean numeric array
$secure = false;
for($i = count($returnArray) - 1; $i > 0; $i--) {
$r = mt_rand(0, $i-1); //subtract 1 from $i to force a new place.
$tmp = $returnArray[$i];
$returnArray[$i] = $returnArray[$r];
$returnArray[$r] = $tmp;
}
return $returnArray;
}
It works very similar to the Fisher-Yates shuffle.
There is just one little difference:
We permit to use the same key, so every entry will get a new place (cause we substract 1 from $i when we do the randomize step).
Working Demo
This question already has answers here:
How to sum all column values in multi-dimensional array?
(20 answers)
Closed 1 year ago.
i am trying to loop through a two-dimensional array and take a sum of the combinations of the columns automatically.
Suppose i have an array called $a with 4 columns:0,1,2,3,
$a=array();
$a[0][0]=1;
$a[0][1]=3;
$a[0][2]=5;
$a[1][0]=10;
$a[1][1]=2;
$a[1][2]=5;
$a[1][3]=7;
$a[2][0]=9;
$a[2][1]=8;
$a[2][2]=9;
$a[2][3]=8;
$a[3][0]=9;
$a[3][1]=8;
$a[3][2]=9;
$a[3][3]=8;
$a[3][4]=1;
And i am trying to sum over all of the combinations of the columns like sum(0,0;1,0;2;0,3;0) etc using this code
for($i=0;$i<count($a[0]);$i++){
for($l=0;$l<count($a[1]);$l++){
for($s=0;$s<count($a[2]);$s++){
for($m=0;$m<count($a[3]);$m++){
echo $sum[]= $a[0][$i]+$a[1][$l]+$a[2][$s]+$a[3][$m];
echo $sum;
echo "<br>";
}
}
}
}
?>
And the code works, the problem is that i am doing these for loops manually, there must be some way in which i can simplify this by somehow inserting the count of the number of columns?
I tried something like
$numberofcolumns=4;
for($n=0;$n<$numberofcolumns;$n++){
for($i=0;$i<count($a[$n]);$i++){
for($m=0;$m<count($a[$n+1]);$m++){
echo $sums[]= $a[$n][$i]+$a[$n+1][$m];
}
}
}
but that doesn't work, there must be some way to simplify the for loops so that i don't have to manually type in the for loops each column
anybody have a clue?
You can use recursion, or just straight nested loops for this, but when working with combinations or permutations, the total number of possibilities can explode and become a huge number, consuming lots of memory to the point where you just cant run the code. Using an iterator is a nice way to trade cpu efficiency for memory efficiency. Here's an iterator I wrote.
class CartesianProductIterator implements Iterator {
protected $iterators;
function __construct(array $iters) {
$this->iterators = $iters;
}
function rewind() {
foreach ($this->iterators as $it) {
$it->rewind();
}
}
function current() {
$values = array();
foreach ($this->iterators as $it) {
$values[] = $it->current();
}
return $values;
}
function key() {
return null;
}
function next() {
/*
loop them in reverse, but exclude first
why? example, odometer: 55199
you always check the rightmost digit first to see if incrementing it would roll it over and need to be "rewound" to 0,
which causes the digit to the left to increase as well, which may also cause it to roll over as well, and so on...
looping in reverse operates from right column to the left.
we dont rewind the first column because if the leftmost column is on its last element and needs to roll over
then this iterator has reached its end, and so rewind() needs to be explicitly called
*/
for ($i = count($this->iterators) - 1; $i > 0; --$i) {
$it = $this->iterators[$i];
$it->next();
if ($it->valid()) {
// were done advancing because we found a column that didnt roll over
return;
} else {
$it->rewind();
}
}
//if execution reached here, then all of the columns have rolled over, so we must attempt to roll over the left most column
$this->iterators[0]->next();
}
function valid() {
return $this->iterators[0]->valid();
}
}
Then use it as
$iterators = array();
foreach ($a as $columnNumber => $values) {
$iterators[] = new ArrayIterator($values);
}
foreach (new CartesianProductIterator($iterators) as $combo) {
// combo has 1 value from each of the ArrayIterators we instantiated
printf("summing %s = %d\n", join('+', $combo), array_sum($combo));
}
heres a demo http://codepad.org/UasdgvWf
You can use RecursiveIteratorIterator
Try
$a = array ();
$a [0] [0] = 1;
$a [0] [1] = 3;
$a [0] [2] = 5;
$a [1] [0] = 10;
$a [1] [1] = 2;
$a [1] [2] = 5;
$a [1] [3] = 7;
$a [2] [0] = 9;
$a [2] [1] = 8;
$a [2] [2] = 9;
$a [2] [3] = 8;
$a [3] [0] = 9;
$a [3] [1] = 8;
$a [3] [2] = 9;
$a [3] [3] = 8;
$a [3] [4] = 1;
$sum = 0;
$array = new RecursiveIteratorIterator ( new RecursiveArrayIterator ( $a ) );
foreach ( $array as $key => $value ) {
$sum += $value;
}
echo $sum;
Output
102
Use $array = new RecursiveIteratorIterator ( new RecursiveArrayIterator ( $a[1] ) ); to get sum of each section ...
If I'm understanding you correctly, this function should do the trick:
<?php
function recursive_sum($arr) {
$sum = 0;
foreach($arr as $value) {
if(is_array($value)) {
$sum += recursive_sum($value);
}
else {
$sum += $value;
}
}
return $sum;
}
?>
Just call recursive_sum($a) to get the sum of all the values in your array, like this:
<?php
echo recursive_sum($a);
?>
<? //PHP 5.4+
$a=[];
$a[0][0]=1;
$a[0][1]=3;
$a[0][2]=5;
$a[1][0]=10;
$a[1][1]=2;
$a[1][2]=5;
$a[1][3]=7;
$a[2][0]=9;
$a[2][1]=8;
$a[2][2]=9;
$a[2][3]=8;
$a[3][0]=9;
$a[3][1]=8;
$a[3][2]=9;
$a[3][3]=8;
$a[3][4]=1;
//This is downright evil, but it works.
eval(\array_reduce(
\array_reverse(\array_keys($a)),
static function($eval, $key){
return "foreach(\$a[$key]as\$i$key)$eval+\$i$key";
},
'{echo$sum[]=0') . ';echo"$sum<br/>";}');
?>
this is my first php script and problem, I've searched hours with no conclusion other than looping a function" too many laterations". but it doesn't solve my problem I've never studied programming or what ever so I'm hoping that there is an educated person to fill me in on this:
I have an array that contains 120 elements; consists of duplicates eg:
myArray = [0]= item_1, [1] = item _1, [2] = item_2, [3] = item_3 ect..
Briefly I'm trying to make a flash php pokermachine but I need these items in the array to be shuffled BUT I do not want the duplicates to be next to each other after the shuffle but I need the duplicates to be still in the array
I can't do a loop function to check this because it will change the shuffle too many times which will effect the odds of the game: below is what I currently have:
/ * Removed the link here that is no longer available */
you may notice at times it will double up with 2 items in the same reel
Basically I created the virtual reel dynamically with php.ini file
these values are repeatedly pushed into an array($virtualreel) so the value may appear 10 times in the reel and another value will appear 5 times variating the odds. Then after I take a random slice() from the $virtualreel to display 3 vars from this reel and repeat the loop 4 more times for the other reels, also I only can shuffle once as I want the slice() to be from the same reels array order
I only shuffle every new spin not running loop functions to shuffle if I double up on a slice(array,3 items).
hope I've explained what I'm after well enough to give you guys an idea.
You can use this function:
<?php
function shuffleArray($myArray) {
$value_count = array_count_values($myArray);
foreach($value_count as $key=>$value) {
if ($value > count($myArray)/2) {
return false;
}
}
$last_value = $myArray[count($myArray) - 1];
unset($myArray[count($myArray) - 1]);
$shuffle = array();
$last = false;
while (count($myArray) > 0) {
$keys = array_keys($myArray);
$i = round(rand(0, count($keys) - 1));
while ($last === $myArray[$keys[$i]]) {
$i = round(rand(0, count($keys) - 1));
}
$shuffle[] = $myArray[$keys[$i]];
$last = $myArray[$keys[$i]];
unset($myArray[$keys[$i]]);
}
if ($last_value === $last) {
$i = 0;
foreach($shuffle as $key=>$value) {
if ($value !== $last_value) {
$i = $key;
break;
}
}
array_splice($shuffle, $i + 1, 0, $last_value);
} else {
$shuffle[] = $last_value;
}
return $shuffle;
}
print_r(shuffleArray(array(1,5,5,3,7,7)));
Why not just:
Edit :
$shuffled = array();
while(count($to_shuffle) > 0):
$i = rand(0, count($to_shuffle)-1);
$shuffled[] = $to_shuffle[$i];
array_splice($to_shuffle, $i, 1,null);
endwhile;
I think this is what you were expecting, if you don't mind not preserving the association between keys and values.