convert numbers to 26base with only letters - php

I need to convert number in "Alphabetic" Counting System, similar to excel spreadsheets:
convert(1) == "A";
convert(2) == "B";
...
convert(26) == "Z";
convert(27) == "AA";
...
convert(52) == "AZ";
convert(53) == "BA";
...
convert(702 ) == "ZZ";
convert(703 ) == "AAA";
this is my code:
function convert($n){
if ($n> 26) {
$tmp = floor($n / 26);
$n= $n % 26;
$result = chr(($tmp - 1) + 65) . chr(($n - 1) + 65);
} else {
$result = chr(($n- 1) + 65);
}
return $result;
}
but the output is a bit off:
#, 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,
AA, AB, AC, AD, AE, AF, AG, AH, AI, AJ, AK, AL, AM, AN, AO, AP, AQ, AR, AS, AT, AU, AV, AW, AX, AY,
B#, BA
I tried to tweak the numbers but I can not make it right, any suggestion for a better algorithm ?

Taking advantage of PHP's ability to increment strings as well as numbers:
function convert($number) {
$result = 'A';
for($i = 1; $i < $number; ++$i) {
++$result;
}
return $result;
}
Demo

Based on #NiettheDarkAbsol comment,
this is a quick solution if the expected calls are for small numbers:
function convert($n) {
for($out = 'A'; --$n > 0; $out++);
return $out;
}
From docs
PHP follows Perl's convention when dealing with arithmetic operations on character variables and not C's. For example, in PHP and Perl $a = 'Z'; $a++; turns $a into 'AA', while in C a = 'Z'; a++; turns a into '[' (ASCII value of 'Z' is 90, ASCII value of '[' is 91). Note that character variables can be incremented but not decremented and even so only plain ASCII alphabets and digits (a-z, A-Z and 0-9) are supported. Incrementing/decrementing other character variables has no effect, the original string is unchanged.

You can try something like this
function convert($n)
{
if ($n <= 0) return "";
$r = "";
$c = "abcdefghijklmnopqrstuvwxyz";
do {
$n -= 1;
$r = $c[$n % 26] . $r;
$n = intval($n / 26);
} while($n > 0);
return strtoupper($r);
}
var_dump(convert(703)); // AAA
var_dump(convert(52)); // AZ
The key is $n -= 1 in every loop because the number system you are using have no concept of zero (0).

Related

Algorithm to find positive and negative integer square roots (without given boundaries)

I've been practicing a lot of algorithms recently for an interview. I was wondering if there was another way to solve this problem. I wrote it in a way where I only increment it positively, because I know from basic math that two negatives multiplied by each other would result to a positive number, so I would just have to make the integer that would satisfy the condition to negative.
Is there a way to write this elegantly where you didn't have the knowledge of multiplying two negative numbers result to a positive?
<?php
# Z = {integers}
# B = {x:x, x is an element of Z, x^2 + 1 = 10}
$numNotFound = true;
$x = 0;
$b = [];
while ($numNotFound) {
if ($x*$x + 1 == 10) {
array_push($b, $x, $x*-1);
$numNotFound = false;
}
$x++;
}
echo json_encode($b); #[3, -3]
Updated
This solution does not use the fact that -1 * -1 = 1. It will output the first number found as the first element in the array. If x=-3 then [-3,3] or if x=3 [3,-3].
$numNotFound = TRUE;
$x = 0;
$b = [];
Do{
if ((pow($x, 2) + 1) === 10) {
array_push($b, $x, 0 - $x);
$numNotFound = FALSE;
}
$x++;
}while($numNotFound);
echo json_encode($b); //[3, -3]

How to list from A to Z in PHP, and then on to AA, AB, AC, etc [duplicate]

This question already has answers here:
PHP range() from A to ZZ?
(17 answers)
Closed 2 years ago.
I know how to list from A to Z:
foreach (range('A', 'Z') as $char) {
echo $char . "\n";
}
But how do I go on from there to list AA, AB, AC, AD, ... AZ, BA, BB, BC and so on?
I did a quick Google search and couldn't find anything, though I guess the approach will be different.
I think I can do it by using a for loop and an array with the letters inside, though that way seems a bit uncouth.
Any other way?
Thanks.
PHP has the string increment operator that does exactly that:
for($x = 'A'; $x < 'ZZ'; $x++)
echo $x, ' ';
Result:
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 AA AB AC AD AE AF...
Ref:
PHP follows Perl's convention when dealing with arithmetic operations on character variables and not C's. For example, in PHP and Perl $a = 'Z'; $a++; turns $a into 'AA', while in C a = 'Z'; a++; turns a into '[' (ASCII value of 'Z' is 90, ASCII value of '[' is 91). Note that character variables can be incremented but not decremented and even so only plain ASCII alphabets and digits (a-z, A-Z and 0-9) are supported. Incrementing/decrementing other character variables has no effect, the original string is unchanged.
http://php.net/manual/en/language.operators.increment.php
Try
foreach (range('A', 'Z') as $char) {
foreach (range('A', 'Z') as $char1) {
echo $char . $char1. "\n";
}
}
PHP follows Perl's convention when dealing with arithmetic operations on character variables.
Hence it is possible to increment alphabets in php
$limit = "AZ";
for($x = "A", $limit++; $x != $limit; $x++) {
echo "$x ";
}
will give you result
A
B
C
.
.
.
AX
AY
AZ
Hope this will help.
I made a constant time function as follows
This function gives the Alphabetic representation of a numeric index
public static $alpha = ['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'];
public static function getColName($index){
$index--;
$nAlphabets = 26;
$f = floor($index/pow($nAlphabets,0)) % $nAlphabets;
$s = (floor($index/pow($nAlphabets,1)) % $nAlphabets)-1;
$t = (floor($index/pow($nAlphabets,2)) % $nAlphabets)-1;
$f = $f < 0 ? '' : self::$alpha[$f];
$s = $s < 0 ? '' : self::$alpha[$s];
$t = $t < 0 ? '' : self::$alpha[$t];
return trim("{$t}{$s}{$f}");
}
Now if you want to use it create a range. you can call this function in a loop pushing your values to an array.
As for most of the time, we need the representation rather than a range this function would work just fine.
HOW TO USE
Just enclose these static functions in a class and use it as
className::getColName(47);
Making a range in my case was a waste of memory.
For Your Case
for($i = 1; $i < 1000; $i++) $range[] = className::getColName($i);
As #Abhijit Srivastava shows, but this is a general way.
function alphaIndex($index) {
$column = "";
$nAlphabets = 26;
$times = (int)($index/$nAlphabets);
$times = $index%$nAlphabets > 0 ? ($times+1):($times);
$index--;
for ($i=0; $i < $times; $i++) {
$less = $i > 0 ? 1:0;
$key = (floor($index/pow($nAlphabets,$i)) % $nAlphabets)-$less;
$column = ( $key<0 ? '':chr(65+$key) ).$column;
}
return $column;
}
I made a custom function that returns an alphabetical range of letters consecutive with a specified amount of letters pass (for example: if you set $pass=2, the function returns [A, C, E, ... AA, AC, AE]).
Another useful option can be $pairs=true that groups all letters into pairs (for example: if you set $pairs=true, the function returns a range of consecutive groups like [[A,B],[C,D],[E,F],...[AA,AB],[AC,AD]] for $pass=1 or [[A,C],[D,F],...[AA,AC],[AD,AF]] for $pass=2).
Call examples:
$myRange = $this->AlphaRange('A','AAZ'); // returns all combinations from A to AAZ,
$myRange = $this->AlphaRange('A','AAZ',2); // returns consecutive combinations from A to AAZ with letters skiped from 2 to 2,
$myRange = $this->AlphaRange('A','AAZ',5,true); // returns consecutive pairs of two letters that contains first and last letter of a group of 5 letters
Hope to be useful.
public function AlphaRange($from, $to, $pass=1, $pairs=false) {
$range = [];
$currStep = 1;
$nextStep = $pass+1;
$currPair = 0;
for($i=$from; $i<'ZZZ'; $i++) {
if ($currStep == 1) {
if (false !== $pairs) {
$range[$currPair][] = $i;
}
else {
$range[] = $i;
}
}
else {
if ($currStep == $nextStep) {
if (false !== $pairs) {
// $range[count($range[$currPair]) == 2 ? ++$currPair : $currPair][] = $i;
$range[$currPair][] = $lastI;
$range[++$currPair][] = $i;
}
else {
$range[] = $i;
}
$currStep = 1;
$nextStep = $pass+1;
}
else {
$lastI = $i;
}
}
if ($i == $to) {
if (false !== $pairs) {
if (count($range[$currPair]) == 1) {
$range[$currPair][] = $i;
}
}
break;
}
$currStep++;
}
return $range;
}

Create fixed length non-repeating permutation of larger set

I know this topic is much discussed but I can't seem to find any implementation that fits my needs.
I have the following set of characters:
a b c d e f g h
I want to get all possible permutations or combinations (non repeating), but on a limited (variable) set of characters, meaning if I input the characters and the number 2, the results should look like
ab ba ac ca ad da ae ea af fa ag ga ah ha
bc cb bd db be eb bf fb bg gb bh hb
cd dc ce ec cf fc cg gc ch hc
de ed df fd dg gd dh hd
ef fe eg ge eh he
fg gf fh hf
gh hg
I hope you understand where I'm going with this. I currently have an implementation that gives me the permutations of all characters, but I can't wrap my head around how to implement a limited space for those permutations:
public function getPermutations($letters) {
if (strlen($letters) < 2) {
return array($letters);
}
$permutations = array();
$tail = substr($letters, 1);
foreach ($this->getPermutations($tail) as $permutation) {
$length = strlen($permutation);
for ($i = 0; $i <= $length; $i++) {
$permutations[] = substr($permutation, 0, $i) . $letters[0] . substr($permutation, $i);
}
}
return $permutations;
}
If you only need one element at a time, you can save on memory by generating each element individually.
If we wanted to generate a random string in your set of expected outputs, we could use this algorithm:
Given a set of characters S, and a desired output length K:
While the output has less than K characters:
Pick a random number P between 1 and |S|.
Append the P'th character to the output.
Remove the P'th character from S.
where |S| is the current number of elements in S.
We can actually encode this sequence of choices into an integer. One way to do that is to change the algorithm as such:
Given a set of characters S, and a desired output length K:
Let I = 0.
While the output has less than K characters:
I = I * (|S| + 1).
Pick a random number P between 1 and the number of elements in S.
I = I + P.
Append the P'th character to the output.
Remove the P'th character from S.
After running this algorithm, the value I will uniquely encode this particular sequence of choices. It basically encodes this as a mixed-radix number; one digit uses base N, the next uses N-1, and so on until the last digit which is base N-K+1 (N being the number of letters in the input).
Naturally, we can also decode this again, and in PHP, that would be something like this:
// Returns the total number of $count-length strings generatable from $letters.
function getPermCount($letters, $count)
{
$result = 1;
// k characters from a set of n has n!/(n-k)! possible combinations
for($i = strlen($letters) - $count + 1; $i <= strlen($letters); $i++) {
$result *= $i;
}
return $result;
}
// Decodes $index to a $count-length string from $letters, no repeat chars.
function getPerm($letters, $count, $index)
{
$result = '';
for($i = 0; $i < $count; $i++)
{
$pos = $index % strlen($letters);
$result .= $letters[$pos];
$index = ($index-$pos)/strlen($letters);
$letters = substr($letters, 0, $pos) . substr($letters, $pos+1);
}
return $result;
}
(Note that for simplicity, this particular decoding algorithm does not correspond exactly to the encoding algorithm I previously described, but maintains the desirable property of a given $index mapping to a unique result.)
To use this code, you would do something like this:
$letters = 'abcd';
echo '2 letters from 4:<br>';
for($i = 0; $i < getPermCount($letters, 2); $i++)
echo getPerm($letters, 2, $i).'<br>';
echo '<br>3 letters from 4:<br>';
for($i = 0; $i < getPermCount($letters, 3); $i++)
echo getPerm($letters, 3, $i).'<br>';
?>
$strings = get_perm( range('a', 'h'), 4 );
function get_perm( $a, $c, $step = 0, $ch = array(), $result = array() ){
if( $c == 1 ){ //if we have last symbol in chain
for( $k = 0; $k < count( $a ); $k++ ){
if( #in_array( $k, $ch ) ) continue; // if $k exist in array we already have such symbol in string
$tmp = '';
foreach( $ch as $c ) $tmp .= $a[$c]; // concat chain of previous symbols
$result[] = $tmp . $a[$k]; // and adding current + saving to our array to return
}
}else{
for( $i = 0; $i < count( $a ); $i++ ){
if( #in_array( $i, $ch ) ) continue;
$ch[$step] = $i; // saving current symbol for 2 things: check if that this symbol don't duplicate later and to know what symbols and in what order need to be saved
get_perm( $a, $c-1, $step+1, $ch, &$result );
// recursion,
// decrementing amount of symbols left to create string,
// incrementing step to correctly save array or already used symbols,
// $ch - array of already used symbols,
// &$result - pointer to result array
}
}
return $result;
}
NOTICE
a-h with 6 symbols = 20k values in array
a-z with 4 symbols = 358799 values in array
So a-z with 10 symbols will die for sure =) It will require too much memory.
You need to try to save output to file or database if you would need big amount of values. Or extend memory limit to php but not sure if this is best way.

How to produce approximate fraction numbers from irrational number on MatLab?

I have a clumsy PHP code that I've used to get approximate fraction numbers for irrational numbers like pi, phi, square root of 2, 3 and so on. I'd like to get a formula that I can use on MatLab and get both data table and draw a plot based on approximate fraction numbers. Maybe someone already can grab from this but I'll provide PHP code to complement the case:
$n = phi(); # irrational number (imaginary/complex number?)
$x = 500; # how many numbers to check
$max = 50; # how many instances to show
$precision = 0.0001;
# check every i against every j and make a comparison how near their values are to each other
for ($i=1; $i<$x; $i++) {
for ($j=1; $j<$x; $j++) {
# compared value is stored on array. very distant numbers needs to be discarded ($precision) or array gets easily too big, limit 64k
if (($d = abs(($n - ($i/$j)))) && $d > $precision) continue;
$c[] = array($i, $j, $d);
}
}
# sort comparison chart by third index (2)
array_qsort($c, 2);
# print max best values from the sorted comparison chart
$count = count($c);
echo "closest fraction numbers for $n from $count calculated values are:<br />\n<br />\n";
$r = 0;
foreach ($c as $abc) {
$r++;
$d = $abc[0]/$abc[1];
echo $abc[0] . '/' . $abc[1] . ' = ' . $d . ' (' . round($abc[2]*(1/$precision), 10) . ')' . "<br />\n";
if ($r > $max) break;
}
There are more efficient algorithms, here is one:
function [a, b, c] = approxfrac( r, precision )
a = floor(r);
r = r - a;
if r==0,
b=0;
c=1;
return
end
p1 = 0; q1 = 1;
p2 = 1; q2 = 1;
b = p1+p2;
c = q1+q2;
while abs(r-b/c) > precision,
if r>b/c,
p1 = b; q1 = c;
else
p2 = b; q2 = c;
end
b = p1+p2;
c = q1+q2;
end
end
There's a function for that: rat

Generating Ordered (Weighted) Combinations of Arbitrary Length in PHP

Given a list of common words, sorted in order of prevalence of use, is it possible to form word combinations of an arbitrary length (any desired number of words) in order of the 'most common' sequences. For example,if the most common words are 'a, b, c' then for combinations of length two, the following would be generated:
aa
ab
ba
bb
ac
bc
ca
cb
cc
Here is the correct list for length 3:
aaa
aab
aba
abb
baa
bab
bba
bbb
aac
abc
bac
bbc
aca
acb
bca
bcb
acc
bcc
caa
cab
cba
cbb
cac
cbc
cca
ccb
ccc
This is simple to implement for combinations of 2 or 3 words (set length) for any number of elements, but can this be done for arbitrary lengths? I want to implement this in PHP, but pseudocode or even a summary of the algorithm would be much appreciated!
Here's a recursive function that might be what you need. The idea is, when given a length and a letter, to first generate all sequences that are one letter shorter that don't include that letter. Add the new letter to the end and you have the first part of the sequence that involves that letter. Then move the new letter to the left. Cycle through each sequence of letters including the new one to the right.
So if you had gen(5, d)
It would start with
(aaaa)d
(aaab)d
...
(cccc)d
then when it got done with the a-c combinations it would do
(aaa)d(a)
...
(aaa)d(d)
(aab)d(d)
...
(ccc)d(d)
then when it got done with d as the 4th letter it would move it to the 3rd
(aa)d(aa)
etc., etc.
<?php
/**
* Word Combinations (version c) 6/22/2009 1:20:14 PM
*
* Based on pseudocode in answer provided by Erika:
* http://stackoverflow.com/questions/1024471/generating-ordered-weighted-combinations-of-arbitrary-length-in-php/1028356#1028356
* (direct link to Erika's answer)
*
* To see the results of this script, run it:
* http://stage.dustinfineout.com/stackoverflow/20090622/word_combinations_c.php
**/
init_generator();
function init_generator() {
global $words;
$words = array('a','b','c');
generate_all(5);
}
function generate_all($len){
global $words;
for($i = 0; $i < count($words); $i++){
$res = generate($len, $i);
echo join("<br />", $res);
echo("<br/>");
}
}
function generate($len, $max_index = -1){
global $words;
// WHEN max_index IS NEGATIVE, STARTING POSITION
if ($max_index < 0) {
$max_index = count($words) - 1;
}
$list = array();
if ($len <= 0) {
$list[] = "";
return $list;
}
if ($len == 1) {
if ($max_index >= 1) {
$add = generate(1, ($max_index - 1));
foreach ($add as $addit) {
$list[] = $addit;
}
}
$list[] = $words[$max_index];
return $list;
}
if($max_index == 0) {
$list[] = str_repeat($words[$max_index], $len);
return $list;
}
for ($i = 1; $i <= $len; $i++){
$prefixes = generate(($len - $i), ($max_index - 1));
$postfixes = generate(($i - 1), $max_index);
foreach ($prefixes as $pre){
//print "prefix = $pre<br/>";
foreach ($postfixes as $post){
//print "postfix = $post<br/>";
$list[] = ($pre . $words[$max_index] . $post);
}
}
}
return $list;
}
?>
I googled for php permutations and got: http://www.php.happycodings.com/Algorithms/code21.html
I haven't looked into the code if it is good or not. But it seems to do what you want.
I don't know what the term is for what you're trying to calculate, but it's not combinations or even permutations, it's some sort of permutations-with-repetition.
Below I've enclosed some slightly-adapted code from the nearest thing I have lying around that does something like this, a string permutation generator in LPC. For a, b, c it generates
abc
bac
bca
acb
cab
cba
Probably it can be tweaked to enable the repetition behavior you want.
varargs mixed array permutations(mixed array list, int num) {
mixed array out = ({});
foreach(mixed item : permutations(list[1..], num - 1))
for(int i = 0, int j = sizeof(item); i <= j; i++)
out += ({ implode(item[0 .. i - 1] + ({ list[0] }) + item[i..], "") });
if(num < sizeof(list))
out += permutations(list[1..], num);
return out;
}
FWIW, another way of stating your problem is that, for an input of N elements, you want the set of all paths of length N in a fully-connected, self-connected graph with the input elements as nodes.
I'm assuming that when saying it's easy for fixed length, you're using m nested loops, where m is the lenght of the sequence (2 and 3 in your examples).
You could use recursion like this:
Your words are numbered 0, 1, .. n, you need to generate all sequences of length m:
generate all sequences of length m:
{
start with 0, and generate all sequences of length m-1
start with 1, and generate all sequences of length m-1
...
start with n, and generate all sequences of length m-1
}
generate all sequences of length 0
{
// nothing to do
}
How to implement this? Well, in each call you can push one more element to the end of the array, and when you hit the end of the recursion, print out array's contents:
// m is remaining length of sequence, elements is array with numbers so far
generate(m, elements)
{
if (m == 0)
{
for j = 0 to elements.length print(words[j]);
}
else
{
for i = 0 to n - 1
{
generate(m-1, elements.push(i));
}
}
}
And finally, call it like this: generate(6, array())

Categories