Weighted percentage between two links? - php

I'm working with two rotating links in a php redirector, is there a way to weight them in percentages?
For example: The first link will have a 70% chance or getting redirected and second 30%.
Code:
<?php
$k = $_GET['sub'];
$aff[] = 'http://google.com';
$aff[] = 'http://yahoo.com';
srand ((double) microtime() * 1000000);
$random_number = rand(0,count($aff)-1);
$lol = ($aff[$random_number]);
$lal = $lol.$k;
header("Location: $lal");
?>
There are similar questions but involve more than two variables.

This seems to work.
$array = array(70 => "http://google.com",
30 => "http://yahoo.com");
$random_number = rand(0, 100);
$last_interval = 0;
$link = "";
foreach( $array as $key => $value ) {
// in range?
if($random_number > $last_interval && $random_number < $key + $last_interval) {
$link = $value;
break;
}
$last_interval = $key;
}
echo "You chose: " . $link;
Concept was taken from https://softwareengineering.stackexchange.com/questions/150616/return-random-list-item-by-its-weight and adapted slightly here is a concept that would work. Credit to #Benjamin Kloster who made the original post.
Prepare a list of intervals that cover 0 to sum(weights, 30 and 70 in your case). Each interval represents one link, its length being it's weight, so for your example:
intervals = [70, 100]
Where an index of 0-70 represents link #1, 70-100 link #2.
Generate a random number n in the range of
0 to sum(weights)
Find the interval in which n falls and you got your link.

Maybe you can do it like this:
$array = array(
array( 30, 'http://google.de/' ),
array( 70, 'http://google.com/' )
);
$number = rand( 1, array_sum( array_column( $array, 0 ) ) );
$lastsum = 0;
foreach( $array as $arr ){
if( $number > $lastsum && $number <= ( $lastsum + $arr[0] ) ){
$url = $arr[1];
break;
}
$lastsum += $arr[0];
}

Related

How to determine if data is increasing or decreasing in PHP

Let's say we have the following data in an array:
$data1 = [3,5,7,6,8,9,13,14,17,15,16,16,16,18,22,20,21,20];
$data2 = [23,18,17,17,16,15,16,14,15,10,11,7,4,5];
As with $data1 we can say the data is increasing while in $data2 it is decreasing.
Using PHP, how do you know the data is increasing or decreasing, and is there a way on how to measure
know the rate of increasing as well as decreasing i.e in terms of percentage.
Edit
From the comments I received I got an idea and here is what I have tried.
What I want to achieve;
I want to know if the trend of the data coming in is upwards or downwards.
Want also to know the rate at which the data is rising or droping. For example $data1 = [1,3,5]; is not the same as $data2 = [1, 20, 55];. You can see $data1 rate of increase is not the same as $data2.
function increaseOrDecrease($streams = []) : array
{
$streams = [3,5,7,6,8,9,13,14,17,15,16,16,16,18,22,20,21,20]; // For the increasing
//$streams = [23,18,17,17,16,15,16,14,15,10,11,7,4,5]; // For the decreasing
$first = 0;
$diff = [];
foreach ($streams as $key => $number) {
if ($key != 0) {
$diff[] = $number - $first;
}
$first = $number;
}
$avgdifference = array_sum($diff)/count($diff); //Get the average
$side = $avgdifference > 0 ? 'UP' : 'DOWN';
$avgsum = array_sum($streams)/count($streams);
$percentage = abs($avgdifference)/$avgsum * 100;
if ($side == 'UP') {
$data = [
'up' => true,
'percent' => $percentage,
];
}else {
$data = [
'up' => false,
'percent' => $percentage,
];
}
return $data;
}
I would like some help to refactor this code or the best approach to solve the issue.
There are several ways to analyze data and extract a trend. The most classical method is called
least squares. It's a way of fitting a line
through the data. The method computes the slope and the intercept of the line. The trend is just the slope.
The formulas are given here.
A PHP implementation is the following:
function linearRegression($x, $y)
{
$x_sum = array_sum($x);
$y_sum = array_sum($y);
$xy_sum = 0;
$x2_sum = 0;
$n = count($x);
for($i=0;$i<$n;$i++)
{
$xy_sum += $x[$i] * $y[$i];
$x2_sum += $x[$i] * $x[$i];
}
$beta = ($n * $xy_sum - $x_sum * $y_sum) / ($n * $x2_sum - $x_sum * $x_sum);
$alpha = $y_sum / $n - $beta * $x_sum / $n;
return ['alpha' => $alpha, 'beta' => $beta];
}
function getTrend($data)
{
$x = range(1, count($data)); // [1, 2, 3, ...]
$fit = linearRegression($x, $data);
return $fit['beta']; // slope of fitted line
}
Examples:
echo getTrend([1, 2, 3]); // 1
echo getTrend([1, 0, -1]); // -1
echo getTrend([3,5,7,6,8,9,13,14,17,15,16,16,16,18,22,20,21,20]); // 1.065
echo getTrend([23,18,17,17,16,15,16,14,15,10,11,7,4,5]); // -1.213
You are asking for a type of data structure that can represent ascending as well as descending data. PHP got SplMinHeap and SplMaxHeap for this purpose. These built in classes make life easer when dealing with ascending or descending datasets.
A quick example ...
<?php
declare(strict_types=1);
namespace Marcel;
use SplMinHeap;
$numbers = [128, 32, 64, 8, 256];
$heap = new SplMinHeap();
foreach ($numbers as $number) {
$heap->insert($number);
}
$heap->rewind();
while($heap->valid()) {
// 8, 32, 64, 128, 256
echo $heap->current() . PHP_EOL;
$heap->next();
}
The SplMinHeap class keeps the minimum automatically on the top. So just use heaps instead of arrays that have no structure. Same goes for SplMaxHeap that keeps the highest value on the top.
Finding the differences
If you want to iterate all data and finding the differences between one to the next, you just have to iterate the heap. It 's ordered anyway.
$heap->rewind();
$smallest = $heap->current();
while($heap->valid()) {
// 8, 32, 64, 128, 256
$current = $heap->current();
echo $current . PHP_EOL;
// 0 (8 - 8), 24 (32 - 8), 32 (64 - 32), 64 (128 - 64), 128 (256 - 128)
echo "difference to the value before: " . ($current - $smallest) . PHP_EOL;
$smallest = $current;
$heap->next();
}
I would do simple things like this
$data1 = [3,5,7,6,8,9,13,14,17,15,16,16,16,18,22,20,21,20];
$data2 = [23,18,17,17,16,15,16,14,15,10,11,7,4,5];
getTrend($data1) //Returns up
getTrend($data2) // Returns down
function getTrend($arr)
{
$up = 0;
$down = 0;
$prev = "";
foreach($arr as $val)
{
if($prev != "" && $val > $prev)
{
$up = $val-$prev;
}
else if($prev != "" && $val < $prev)
{
$down = $prev-$val ;
}
$prev = $val);
}
if($up > $down)
{
return "up";
}
else if($down > $up)
{
return "down";
}
else {
return "flat";
}
}

How can I generate and validate random IDs using the Saudi ID format?

I need to generate random IDs that validate against the criteria for Saudi IDs shown in this question:
Saudi Iqama/National Identity number field validation
I've tried the following code:
$random_numbers = [];
while(count($random_numbers) < 1000000000){
do {
$random_number = mt_rand(1000000000,9000000000);
}
while (in_array($random_number, $random_numbers));{
$type = substr ( $random_number, 0, 1 );
if($type != 2 && $type != 1 ) break;
$sum = 0;
for( $i = 0 ; $i<10 ; $i++ ) {
if ( $i % 2 == 0){
$ZFOdd = str_pad ( ( substr($random_number, $i, 1) * 2 ), 2, "0", STR_PAD_LEFT );
$sum += substr ( $ZFOdd, 0, 1 ) + substr ( $ZFOdd, 1, 1 );
}else{
$sum += substr ( $random_number, $i, 1 );
}
}
return $sum%10 ? break : echo $random_number;
----------
echo "<br>";
$random_numbers[] = $random_number;}
}
Disclaimer: I'm not 100% sure on the validation required etc. for Saudi ID numbers and have only briefly looked at the answers supplied in the linked question
Okay, so, my understanding is that you need to generate a random id that:
Matches the pattern/format:
[12]\d{9}
Validates against the criteria show in the linked question:
Saudi Iqama/National Identity number field validation
To do this we need to create a couple of functions; one to generate IDs and one to validate the IDs against the given criteria.
Generate the ID
Simply generating an ID is simple enough. We can use the random_int function in PHP with a loop. If we enclose the code to generate the ID inside of a do...while... loop then we can execute the code and validate the ID repeatedly until we get a valid one.
function getRandomSaudiId() : int
{
do {
$saudiId = (string) random_int(1,2);
for($i = 0; $i < 9; $i++){
$saudiId .= random_int(0,9);
}
} while(validateSaudiId($saudiId) === false);
return (int) $saudiId;
}
Validate the ID
Note: we convert to string so that we can access the numbers based on their index.
function validateSaudiId(string $id) : bool
{
$sum = 0;
for($i = 0; $i < 9; $i++){
if( $i % 2 ){
// Even number
$sum += $id[$i];
}
else{
//Odd number
$increment = $id[$i] * 2;
while($increment > 9){
$increment = (string) $increment;
$increment = $increment[0] + $increment[1];
}
$sum += $increment;
}
}
$sum = (string) $sum;
return ($sum[1] == $id[9] || $id[9] == (10 - $sum[1])) ? true : false;
}
Example use
for($i = 0; $i < 10; $i++) var_dump(getRandomSaudiId());
/*
Output:
int(2933617506)
int(2409806096)
int(1072585118)
int(2891306413)
int(1810304558)
int(2591965856)
int(1363032527)
int(1031823269)
int(1265954048)
int(2498099472)
int(1134172537)
*/

Break down number into thousands,hundreds,etc

I'm currently putting together a cheque printing solution for my company. When printing the cheque you need to print the number of millions,hundred thousands,ten thousands,thousands, hundreds,tens and units (pounds/dollars/euros etc ) from the amount being paid.
In the case of 111232.23 the following is correctly output from the code I have written below. I cant help feeling that there is a more efficient or reliable method of doing this? Does anyone know of a library/class math technique that does this?
float(111232.23)
Array
(
[100000] => 1
[10000] => 1
[1000] => 1
[100] => 2
[10] => 3
[1] => 2
)
<?php
$amounts = array(111232.23,4334.25,123.24,3.99);
function cheque_format($amount)
{
var_dump($amount);
#no need for millions
$levels = array(100000,10000,1000,100,10,1);
do{
$current_level = current($levels);
$modulo = $amount % $current_level;
$results[$current_level] = $div = number_format(floor($amount) / $current_level,0);
if($div)
{
$amount -= $current_level * $div;
}
}while($modulo && next($levels));
print_r($results);
}
foreach($amounts as $amount)
{
cheque_format($amount);
}
?>
I think you just re-wrote the number_format function that PHP has. My suggestion is to use the PHP function rather than to re-write it.
<?php
$number = 1234.56;
// english notation (default)
$english_format_number = number_format($number);
// 1,235
// French notation
$nombre_format_francais = number_format($number, 2, ',', ' ');
// 1 234,56
$number = 1234.5678;
// english notation without thousands separator
$english_format_number = number_format($number, 2, '.', '');
// 1234.57
?>
I'm not sure exactly what the PHP script would be for this, but if you have 10000, 1000, 100, 10, 1 as the things you need the amounts of. How many 10,000's in amount $dollar?
floor($dollar/10000)
how many thousands?
floor(($dollar%10000)/1000)
etc.
This is not the answer for the question, but the following also break down the decimals.
function cheque_format($amount, $decimals = true, $decimal_seperator = '.')
{
var_dump($amount);
$levels = array(100000, 10000, 1000, 100, 10, 5, 1);
$decimal_levels = array(50, 20, 10, 5, 1);
preg_match('/(?:\\' . $decimal_seperator . '(\d+))?(?:[eE]([+-]?\d+))?$/', (string)$amount, $match);
$d = isset($match[1]) ? $match[1] : 0;
foreach ( $levels as $level )
{
$level = (float)$level;
$results[(string)$level] = $div = (int)(floor($amount) / $level);
if ($div) $amount -= $level * $div;
}
if ( $decimals ) {
$amount = $d;
foreach ( $decimal_levels as $level )
{
$level = (float)$level;
$results[$level < 10 ? '0.0'.(string)$level : '0.'.(string)$level] = $div = (int)(floor($amount) / $level);
if ($div) $amount -= $level * $div;
}
}
print_r($results);
}

Simple random generator script not working

I have this script but it's not working, not sure how to fix it, any tips please? all I get out the other end is the letter "t"
Thanks :)
<?php
$cachefile = './current_t_id';
$time = $id = null; // assume we have no cached quote
$change_every = 3600; // seconds
$pages = array(1 => 'text1-1.php', 2 => 'text1-2.php');
foreach($pages as $pagekey => $page){
if($pagekey == $siteId){
include($page);
}
}
// if cached quote exists, load it
if(is_file($cachefile)) {
list($time, $id) = explode(' ', file_get_contents($cachefile));
}
// if no cached quote or duration expired, change it
if(!$time || time() - $time > $change_every) {
srand ((double) microtime() * 100000);
$id = rand(0,count($page)-1);
file_put_contents($cachefile, time().' '.$id); // update cache
}
// echo the quote, be it cached or freshly picked
echo ($page[$id]);
?>
OK am going to give you 3 different examples
Variables
$quotes = array (
"hello",
"baba",
"luke"
);
$pages = array(1 => 'text1-1.php', 2 => 'text1-2.php');
A. Using Random Quotes
// Using Ramdom Quptes
$key = array_rand ( $quotes );
echo $quotes [$key];
//Or
include($pages[$key]) ;
B. Using Robin
// Using Robin
$cacheFile = "robin.cache";
$robin = null;
$quotesID = intval ( file_get_contents ( $cacheFile ) );
$totalQuotes = count ( $quotes );
$key = ($quotesID < ($totalQuotes - 1)) ? $quotesID ++ : 1;
file_put_contents ( $cacheFile, $quotesID );
echo $quotes [$key];
//Or
include($pages[$key]) ;
Using Timer
// Using Timer
$cacheFile = "timer.cache";
$expiration = 3600;
$robin = null;
list($quotesID, $time) = explode(' ', file_get_contents($cacheFile));
$totalQuotes = count ( $quotes );
if($time < (time() - $expiration))
{
$key = mt_rand(0,count($pages)-1);
file_put_contents($cacheFile, time().' '.$id);
}
echo $quotes [$key];
//Or
include($pages[$key]) ;
I hope your issue can be resolved now
Thanks
:)

Encoding like base36 including uppercase

I am using base36 to shorten URLs. I have an id of a blog entry and convert that id to base36 to make it smaller. Base36 only includes lowercase letters. How can I include uppercase letters? If I use base64_encode it actually makes the string longer.
you can find examples of source-code to create short-urls containing letters (both lower and upper case) and number on those two articles, for instance :
Create short IDs with PHP - Like Youtube or TinyURL
Building a URL Shortener
Here is the portion of code used in that second article (quoting) :
$codeset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
$base = strlen($codeset);
$n = 300;
$converted = "";
while ($n > 0) {
$converted = substr($codeset, ($n % $base), 1) . $converted;
$n = floor($n/$base);
}
echo $converted; // 4Q
And you can pretty easily encapsulate this in a function -- only thing to consider is that $n is to be received as a parameter :
function shorten($n) {
$codeset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
$base = strlen($codeset);
$converted = "";
while ($n > 0) {
$converted = substr($codeset, ($n % $base), 1) . $converted;
$n = floor($n/$base);
}
return $converted;
}
And calling it this way :
$id = 123456;
$url = shorten($id);
var_dump($url);
You get :
string 'w7e' (length=3)
(You can also add some other characters, if needed -- depending on what you want to get in your URLs)
Edit after the comment :
Reading through the second article (from which I got the shortening code), you'll find the code that does the un-shortening.
Encapsulating that code in a function shouldn't be that hard, and might get you something like this :
function unshorten($converted) {
$codeset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
$base = strlen($codeset);
$c = 0;
for ($i = strlen($converted); $i; $i--) {
$c += strpos($codeset, substr($converted, (-1 * ( $i - strlen($converted) )),1))
* pow($base,$i-1);
}
return $c;
}
And calling it with a shortened-url :
$back_to_id = unshorten('w7e');
var_dump($back_to_id);
Will get you :
int 123456
function dec2any( $num, $base=62, $index=false ) {
// Parameters:
// $num - your decimal integer
// $base - base to which you wish to convert $num (leave it 0 if you are providing $index or omit if you're using default (62))
// $index - if you wish to use the default list of digits (0-1a-zA-Z), omit this option, otherwise provide a string (ex.: "zyxwvu")
if (! $base ) {
$base = strlen( $index );
} else if (! $index ) {
$index = substr( "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" ,0 ,$base );
}
$out = "";
for ( $t = floor( log10( $num ) / log10( $base ) ); $t >= 0; $t-- ) {
$a = floor( $num / pow( $base, $t ) );
$out = $out . substr( $index, $a, 1 );
$num = $num - ( $a * pow( $base, $t ) );
}
return $out;
}
Shamelessly borrowed from a commenter on PHP's base_convert() page (base_convert() only works up to base 32).

Categories