I can't think a way in PHP to divide number into ranges.
I have massive MySQL database record set and want to spread some workload for my scripts.
Example:
There is 2435 rows in table, I want that each PHP script select only specific range of records:
Script 1: select [1 to 150];
Script 2: select [151 to 270];
The main problem: I can't think a method how to divide that 2435 into even ranges and pass them into MySQL SELECT.
Try Like this way-
SELECT * FROM table_name ORDER BY id ASC LIMIT 150 OFFSET 0
For 1st script,
LIMIT 150 OFFSET 0
then For 2nd script
LIMIT 150 OFFSET 150
So on...
So, for a few days I was testing how offset behaves with massive data sets (100+k rows) and it was terrible. Offset is insanely resource hungry and definitely not for the task if you have large number of rows in your database.
My final code looks like this (abstraction):
chunker.php
// Divide table rows into 50 chunks and produce array
$rows = DB::query("SELECT id FROM data_set GROUP BY id DESC");
$chunks = array_chunk($rows, 50, TRUE);
// Extract endings of each chunk array
$ends = array();
foreach ($chunks as $c) {
$f = flattenArray($c);
$arr_end = end($f);
array_push($ends, $arr_end);
}
// Spawn separate PHP processes to work with a chunk array ending
foreach ($ends as $e) {
$bb = shell_exec("php -q worker.php '".$e."' > /dev/null &");
}
worker.php
// Catch argv
$exec_args = $_SERVER['argv'];
// Select predefined amount of rows from DB which is more than or equal to argv value
$data = DB::query("SELECT * FROM data_set WHERE id >= %i LIMIT 50", $exec_args[1]);
foreach ($data as $d) {
// Do you stuff here
}
This adaptation came from this article http://mysql.rjweb.org/doc.php/pagination
Performance wise I offseting data required 8 CPU cores with 32 GB RAM. With LIMIT method I only need 4 GB RAM and 2 CPU's. So, think twice before using offset in LARGE data sets.
Related
Okay, so i don't really know how I go about this.
I'm currently working on a lottery system for a game.
I have a table with virtual items which I want to randomly select by a likely chance.
Table examples:
ID = 1, item_name = Sword, likely_chance = 75
ID = 2, Item_name = 10,000, likely_chance = 20
For id 2, 10,000 represents 10,000 coins.
I want to come up with an algorithm which will select a item with a higher chance of selecting a higher likely chance but also still be able to win a item with a lower likely chance rarely.
If you have items with "likely chances" of C1, C2, C3...Cn, then you can calculate the sum Ctotal.
Then, you can get a random value between 0 and Ctotal, and walk through your array (order is irrelevant) until the sum of "skipped" items exceeds this random value.
For example, in your case, Ctotal = 75 + 20 = 95. Get a random number between 0 and 95, and if it is less than 75 - give a sword; else - give 10000 coins. It will provide a fair winnings distribution according to your likely chances - 78.95% and 21.05%, respectively.
$items = ['Sword', '10000 coins'];
$chances = [70, 25];
$ctotal = array_sum($chances); echo "Total is $ctotal\n";
$rand = rand(0, $ctotal); echo "Rand is $rand\n";
$i = 0;
$currentSum = 0;
while (true)
{
$currentSum += $chances[$i];
if ($currentSum >= $rand)
{
echo "You win: ".$items[$i];
break;
}
$i++;
}
Here is the working Demo. Note that IDEOne remembers the last output and doesn't run this program again every time. The output will appear to be the same, but it is not.
Say I have a list of 65 strings.
I need to split this single list into multiple "pools" that have a similar amount of strings.
The amount cannot be over 32.
In this list of 65, they're all ranked from 1st to 65th.
For a list of 65 strings, it'd split into one pool of 21, and two pools of 22.
For a list of 34 strings, it'd split into one pool of 18, and one pool of 18.
For a list of 115 strings, it'd split into one pool of 28, and three pools of 29.
And so on.
However, the new lists need to be fairly ranked.
In example it should be like so:
rank 1 in pool 1
rank 2 in pool 2
rank 3 in pool 3
rank 4 in pool 1
rank 5 in pool 2
rank 6 in pool 3
This way, rank 1 and rank 4, become rank 1 and 2 in their new list.
Same goes for the rest.
I'm thinking I'd need to use array_chunk in combination with a modulo operation, but I can't seem to wrap my head around it.
This is not as difficult as it might seem:
// settings
$cellCount = 115;
$maxPoolSize = 32;
// create test array with numbered strings
$testArray = array_fill(1,$cellCount,'Cell ');
foreach ($testArray as $key => $value) $testArray[$key] .= $key;
// determine the number of pools needed
$arraySize = count($testArray);
$poolCount = ceil($arraySize/$maxPoolSize);
// fill the pools
$poolNo = 0;
foreach ($testArray as $cell)
{
$poolArray[$poolNo][] = $cell;
$poolNo++;
if ($poolNo == $poolCount) $poolNo = 0;
}
// show result
echo '<pre>';
print_r($poolArray);
echo '</pre>';
I'm sure there are other solutions, but this seems to do the job.
I think you're on the right way, by using array_chunk and a modulo operation, that's the way i would choose.
It would be like:
$countarray = count($myarray);
$modulo = 2;
while ($countarray>32)
{
$result = $countarray/$modulo;
if($result>32)
$modulo++;
}
$newpool = array_chunk($myarray, $modulo);
I'm not a god in php, so i hope it will help ! Sorry for my poor english.
Not sure if this is possible with just php or if I'd need something else too. Basically I'd like to have an array with a bunch of entries, such as:
(1234,5432,5678,5899,3245)
And I'd like to have the php randomly select one entry from the array AND stick to that entry it selected for 3 days. So for example, on a given date, it would pull "5432" from the array, then it would hold that entry for 3 days, then after 3 days it would pick another entry and hold it for 3 days, etc.
Any ideas how this could be done? Can it be done with just php? Thanks so much for any help.
Assuming you're running a PHP program (command line), and not running a script on a web server you can run this script to stay on all the time and wait to draw.
$entries = array( 34534, 435, 345 );
while(1) {
// subtract 1 from total number of entries because arrays start at an index of 0.
$totalNumberOfEntries = sizeof( $entries ) - 1;
// if no entries left, quit the program
if ($totalNumberOfEntries <= 0) break;
// grab a random index from your array using `mt_rand()` function
$entry = $entries[ mt_rand(0, $totalNumberOfEntries - 1) ];
// write the entry to a file
file_put_contents( 'entry.txt', $entry, FILE_APPEND );
// wait 3 days to draw again
sleep( 3600 * 24 * 3 );
}
I have something with pow I need to return a whole inter for different results (reversed basically)
For Example (rounded to 4 decimals):
<?php
$var = 10 * pow(1.01,0); // = 10
$var = 10 * pow(1.01,1); // = 10.1
$var = 10 * pow(1.01,2); // = 10.201
$var = 10 * pow(1.01,3); // = 10.303
etc
How could I return the whole number for 10.1150 = 1 since it is above 10 * pow(1.01,1); and below 10 * pow(1.01,2);
The 1.01 can also be a number of variables, (percentage scaled point and figure charting), why I need the Whole number only of the percentage differences (1 box = 1 percentage change from the box above or below)
Thanks much all, I've battled with this issue in many different ways including x all price data points for 250 percentage changed into an array, that is fast, except to build the array becomes the slow issue. if else is the current solution but for 2500 price points on 250 charts = 2 seconds just to find a plot point, considering I'm doing 10's of millions of charts daily, 2 seconds = a lot of time accumulated.
Have you tried using log? It's the reverse of the pow function.
$orig = floor(log($var/10,1.01));
Haven't programmed in a while and here I am with this simple problem, basically I need to generate two random numbers that are within a maximum limit, but with set distance apart from each other in order to randomly (each time) call fields from the database using the mysql LIMIT.
ex. say there are 95 database fields ($max), fields returned = 20 ($fields) (number of fields that should be returned) or range limit 20 (ie difference between min - max is 20)
SELECT............... LIMIT $a, $fields
$a - $b = 20
$b < 95
I just use ...
$a = floor(mt_rand(0, ($max-$fields)));
to generate an $a and use the $fields value which gives me all the info I need (ie from where to start getting the records ($a) and how many records to get ($fields))
Any ideas on another way on how to do this?
If your table is always bigger then the required distance of the two random numbers you can go about it like this:
// $row_count is the number of rows you have in the database
$max = mt_rand($distance, $row_count);
$min = $max - $distance;
// now you can use these in sql like
$sql = "select ... offset {$min} limit {$distance}";
References: mt_rand