I am trying to generate some SKU numbers and I came to an issue that has made me think, and as I slept less than 2 hours I decided to ask you guys, Stackoverflowers.
Let's say I've got an array of the alphabet excluding commonly mistaken letters.
$alphabet = array("A","C","D","E","F","G","H","I","J","K","L","M","N","P","Q","R","S","T","U","V","W","X","Y","Z");
I am trying to generate 2 letters based on consistent number. Let's say I've got sub-products that I want to have that suffix in the end of their SKU. For the first sub-product the SKU will have suffix - AA, for the 24th - AZ, 25th - CA, 26th - CC and so on. The thing is that we don't want to have repeating suffixes, but AC and CA are acceptable.
Thank you for doing the dirty job for a sleep needing programmer.
Making it clear:
I want to get a combination based on irritation. Let's say:
$i = 1, then $suffix = AA;
$i = 2, then $suffix = AC;
$i = 24, then $suffix = ZZ;
$i = 25 (one above the count of the array), then $suffix = CA;
$i = 26, then $suffix = CC;
$i = 49, then $suffix = DA (**I suppose**)
Let's say I have sub-products for product 1 and sub-products for product 2. Product 1's sub-products' suffixes should be:
AA, AC, AD, AE .... AZ, CA, CC, CD .... CZ .... ZA, ZC ... ZY.
Product 2's sub-products' suffixes can also be the same!
I would use the product number to pick two indexes from the list of available letters The following can be done in one line, but I expand it so I can explain what it does.
function get2letter($num)
{
$alphabet = array("A","C","D","E","F","G","H","I","J","K","L","M","N","P","Q","R","S","T","U","V","W","X","Y","Z");
$code = $alphabet[$num%sizeof($alphabet)]; // Get the first letter
$num = floor($num/sizeof($alphabet)); // Remove the value used to get the first letter
$code.= $alphabet[$num%sizeof($alphabet)]; // Get the second letter
return $code;
}
for($i=0; $i<10; $i++)
print get2letter($i)."\n";
This will work for small values. You have collisions when you surpass the number of unique values you can represent with your alphabet.
I think that's not a good conception because you will be limited in the time with this solution.. what I mean is that it's not unlimited.
If you really want to do this, I think you have to construct a kind of array with all the solution and index it with a number and when you create a new product, just know the number of product you have and take the next one.
Related
I am working in php and I am trying to create 1000 tickets in a database. Each ticket needs it's own unique code that consists of letters and numbers about 6 characters long.
EXP.
Tbl_Tickets
ID code
1 3F2jk7
2 2HGUF1
3 9FJDNJ
4 MFJEY9
5 23988D
I was wondering is there a simple way of doing this with php, or excel, or any other way for that matter. I know that i can use a random number generator, but the check for the Unique would have a large BigO notation and the check would get messy.
Unique is not compatible with random, but the following might suit:
=CHOOSE(RANDBETWEEN(1,2),RANDBETWEEN(0,9),CHAR(RANDBETWEEN(65,90)))
copied across to populate six columns (say A to F) with, in G:
=A1&B1&C1&D1&E1&F1
and both copied down to say row 1100. Then select G, copy Paste Special Values, and Remove Duplicates on ColumnG and select first 1000 entries.
You could easily create an array of strings in php and write it to a database:
function generateRandomString($length = 6, $letters = '1234567890QWERTYUIOPASDFGHJKLZXCVBNM'){
$s = '';
$lettersLength = strlen($letters)-1;
for($i = 0 ; $i < $length ; $i++){
$s .= $letters[rand(0,$lettersLength)];
}
return $s;
}
// Create an array with random strings
for ($i=0; $i<1000; $i++){
$ticket_numbers = array();
$ticket_number = generateRandomString();
while (in_array($ticket_number,$ticket_numbers))
$ticket_number = generateRandomString();
$ticket_numbers[] = $ticket_number;
}
// Write the array to a database
$con = mysqli_connect("myhost","myuser","mypassw","mybd") or die("Error");
foreach ($ticket_numbers as $number){
mysqli_query($con,"Your insert query using the value $number");
}
mysqli_close($con);
This should help you in the right direction though there are probably better ways to do this.
The function generateRandomString() was taken from How to generate random numbers/letters with PHP/Javascript
And another option. Encryption is guaranteed to be unique, so encrypting the numbers 0, 1, 2, ... will give you guaranteed unique random-seeming output. Six characters is 30 bits using Base32, or 36 bits using Base64. You will need a 30 (or 36 bit) cypher. Unless you have a library that includes Hasty Pudding cypher (unlikely) then just implement a simple four round Feistel cypher with the appropriate block size. It will not be completely secure, but it will be enough to defeat casual attacks.
This will produce random strings in column B with no repeats from B1 thru B1001
Sub Lottery()
Dim i As Long, j As Long, c As Collection
Set c = New Collection
v = Split("0,1,2,3,4,5,6,7,8,9,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", ",")
For i = 1 To 5000
can = ""
For j = 1 To 6
can = can & v(Application.RandBetween(0, 35))
Next j
On Error Resume Next
c.Add can, CStr(can)
On Error GoTo 0
If c.Count = 1000 Then Exit For
Next i
For i = 1 To 1000
Cells(i + 1, 2).Value = c(i)
Next i
End Sub
I want a hint on a efficient algorithm of solving the following:
(Okay, The question was not that, I just made this for simplicity).
Example: Out of N Advert boards there should be M advert of a company in K consecutive boards. and the Adverts will be used such a way that only MINIMUM number of Adverts will be used
if N=3,K=2,M=1. The answer will be one 010;
if N=6, K=3, M=2 the answer will be 6.
011011,
011101,
011110,
101101,
101110,
110110.
I took the approach of Creating all the combinations(with binary approach) with iterative and recursive method.After that I filtered it. It is working well except if N is big it will crash.(as expected). So Is there any efficient method of solving these?
I just took another approach but it is not going to well .. :(
Second approach
if($n%$k == 0){
$perm = $m*($n/$k);
}else{
$perm = $m*ceil($n/$k);
}
Then I will do the nCr... and now I'm lost
First Approach
<?php
$n = $input1;$k = $input2;$l=$input3; $total = 0;
$flag = false;$arrays = array();$z=0;$min=$n;$x=0;
$total_permutations = pow(2,$n);
for($i=0;$i<$total_permutations;$i++){
$binary = base_convert($i, 10, 2);
$binary = str_pad($binary, $n,"0", STR_PAD_LEFT);
for($j=0;$j<=($n-$k);$j++){
$flag = false;$x=0;
for($m=0;$m<$k;$m++){
$x += intval($binary{$j+$m});
}
if($x<$l){
$flag = true;
break;
}
}
if(!$flag){
$arrays[$z]=str_split($binary);
$arrays[$z] = array_map('intval', $arrays[$z]);
$z++;
echo $binary."<br />";
}
unset($binary);
}
$min = min(array_map('array_sum',$arrays));
echo "------------<br />";
foreach($arrays as $val){
$sum = array_sum($val);
if($sum == $min){
echo implode("",$val);echo "<br>";
$total++;
}
}
return $total;
}
?>
To elaborate on my comment, here is one potential solution (Though, I would be very disappointed if there wasn't a better one)
1) First, calculate the minimum number of 1's needed in a valid solution. (I believe this is simply max(M, floor(n/k)*m + max(0, n%k-(k-m))). The formula is derived from building a string one letter at a time, placing 0's whenever possible)
2) Now, assume N > K (otherwise the answer is trivial).
We define the sub-problem to be: "Given a prefix P of length K, and a budget B of 1's to place, how many ways are there to fill out N characters whilst still enforcing the M rule?"
For example, consider that our string is "101XXXXXX", with K = 3, M = 2, and B = 4. Here, N = 6, P = "101". We solve this sub-problem like so:
a) Check if the budget is big enough in O(N) time. Return 0 if it isn't
If N=0, return 1 trivially
b) Set total possible solutions to 0
c) Set the first unknown character to 1. Compute the new prefix P'
(by stripping off the first character, and adding a "1" to the end),
the new B' = B-1, the new N' = N-1, and solve the new sub-problem:
Add its result to our total
d) Set the unknown character to 0 instead: But only if the new prefix P'
(strip, add "0") has at least M 1's. If so, set B' = B-1, N' = N-1,
and solve the new sub-problem. Add its result to our total
e) Return the total
To solve the original problem, simply consider all possible prefixes P (all nCr(K,M) of them), and sum up the solutions to the derived sub-problems. By caching the results to sub-problems based on unique inputs P, N, and B, we reduce the amount of duplicate work drastically. Amortized analysis should show that the full solution runs in O(N*nCr(K,M)) time
I am working on a Web Application that includes long listings of names. The client originally wanted to have the names split up into divs by letter so it is easy to jump to a particular name on the list.
Now, looking at the list, the client pointed out several letters that have only one or two names associated with them. He now wants to know if we can combine several consecutive letters if there are only a few names in each.
(Note that letters with no names are not displayed at all.)
What I do right now is have the database server return a sorted list, then keep a variable containing the current character. I run through the list of names, incrementing the character and printing the opening and closing div and ul tags as I get to each letter. I know how to adapt this code to combine some letters, however, the one thing I'm not sure about how to handle is whether a particular combination of letters is the best-possible one. In other words, say that I have:
A - 12 names
B - 2 names
C - 1 name
D - 1 name
E - 1 name
F - 23 names
I know how to end up with a group A-C and then have D by itself. What I'm looking for is an efficient way to realize that A should be by itself and then B-D should be together.
I am not really sure where to start looking at this.
If it makes any difference, this code will be used in a Kohana Framework module.
UPDATE 2012-04-04:
Here is a clarification of what I need:
Say the minimum number of items I want in a group is 30. Now say that letter A has 25 items, letters B, C, and D, have 10 items each, and letter E has 32 items. I want to leave A alone because it will be better to combine B+C+D. The simple way to combine them is A+B, C+D+E - which is not what I want.
In other words, I need the best fit that comes closest to the minimum per group.
If a letter contains more than 10 names, or whatever reasonable limit you set, do not combine it with the next one. However, if you start combining letters, you might have it run until 15 or so names are collected if you want, as long as no individual letter has more than 10. That's not a universal solution, but it's how I'd solve it.
I came up with this function using PHP.
It groups letters that combined have over $ammount names in it.
function split_by_initials($names,$ammount,$tollerance = 0) {
$total = count($names);
foreach($names as $name) {
$filtered[$name[0]][] = $name;
}
$count = 0;
$key = '';
$temp = array();
foreach ($filtered as $initial => $split) {
$count += count($split);
$temp = array_merge($split,$temp);
$key .= $initial.'-';
if ($count >= $ammount || $count >= $ammount - $tollerance) {
$result[$key] = $temp;
$count = 0;
$key = '';
$temp = array();
}
}
return $result;
}
the 3rd parameter is used for when you want to limit the group to a single letter that doesn't have the ammount specified but is close enough.
Something like
i want to split in groups of 30
but a has 25
to so, if you set a tollerance of 5, A will be left alone and the other letters will be grouped.
I forgot to mention but it returns a multi dimensional array with the letters it contains as key then the names it contains.
Something like
Array
(
[A-B-C-] => Array
(
[0] => Bandice Bergen
[1] => Arey Lowell
[2] => Carmen Miranda
)
)
It is not exactly what you needed but i think it's close enough.
Using the jsfiddle that mrsherman put, I came up with something that could work: http://jsfiddle.net/F2Ahh/
Obviously that is to be used as a pseudocode, some techniques to make it more efficient could be applied. But that gets the job done.
Javascrip Version: enhanced version with sort and symbols grouping
function group_by_initials(names,ammount,tollerance) {
tolerance=tollerance||0;
total = names.length;
var filtered={}
var result={};
$.each(names,function(key,value){
val=value.trim();
var pattern = /[a-zA-Z0-9&_\.-]/
if(val[0].match(pattern)) {
intial=val[0];
}
else
{
intial='sym';
}
if(!(intial in filtered))
filtered[intial]=[];
filtered[intial].push(val);
})
var count = 0;
var key = '';
var temp = [];
$.each(Object.keys(filtered).sort(),function(ky,value){
count += filtered[value].length;
temp = temp.concat(filtered[value])
key += value+'-';
if (count >= ammount || count >= ammount - tollerance) {
key = key.substring(0, key.length - 1);
result[key] = temp;
count = 0;
key = '';
temp = [];
}
})
return result;
}
Background;
to create a dropdown menu for a fun gambling game (Students can 'bet' how much that they are right) within a form.
Variables;
$balance
Students begin with £3 and play on the £10 table
$table(there is a;
£10 table, with a range of 1,2,3 etc to 10.
£100 table with a range of 10,20,30 etc to 100.
£1,000 table with a range of 100, 200, 300, 400 etc to 1000.)
I have assigned $table to equal number of zeros on max value,
eg $table = 2; for the £100 table
Limitations;
I only want the drop down menu to offer the highest 12 possible values (this could include the table below -IMP!).
Students are NOT automatically allowed to play on the 'next' table.
resources;
an array of possible values;
$a = array(1,2,3,4,5,6,7,8,9,10,20,30,40,50,60,70,80,90,10,20,30,40,50,60,70,80,90,100,200,300,400,500,600,700,800,900,1000);
I can write a way to restrict the array by table;
(the maximum key for any table is (9*$table) )//hence why i use the zeroes above (the real game goes to $1 billion!)
$arrayMaxPos = (9*$table);
$maxbyTable = array_slice($a, 0, $arrayMaxPos);
Now I need a way to make sure no VALUE in the $maxbyTable is greater than $balance.
to create a $maxBet array of all allowed bets.
THIS IS WHERE I'M STUCK!
(I would then perform "array_slice($maxBet, -12);" to present only the highest 12 in the dropdown)
EDIT - I'd prefer to NOT have to use array walk because it seems unnecessary when I know where i want the array to end.
SECOND EDIT Apologies I realised that there is a way to mathematically ascertain which KEY maps to the highest possible bid.
It would be as follows
$integerLength = strlen($balance);//number of digits in $balance
$firstDigit = substr($balance, 0, 1);
then with some trickery because of this particular pattern
$maxKeyValue = (($integerlength*9) - 10 + $firstDigit);
So for example;
$balance = 792;
$maxKeyValue = ((3*9) - 10 + 7);// (key[24] = 700)
This though works on this problem and does not solve my programming problem.
Optional!
First of all, assuming the same rule applies, you don't need the $a array to know what prices are allowed on table $n
$table = $n; //$n being an integer
for ($i = 1; $i <= 10; $i++) {
$a[] = $i * pow(10, $n);
}
Will generate a perfectly valid array (where table #1 is 1-10, table #2 is 10-100 etc).
As for slicing it according to value, use a foreach loop and generate a new array, then stop when you hit the limit.
foreach ($a as $value) {
if ($value > $balance) { break; }
$allowedByTable[] = $value;
}
This will leave you with an array $allowedByTable that only has the possible bets which are lower then the user's current balance.
Important note
Even though you set what you think is right as options, never trust the user input and always validate the input on the server side. It's fairly trivial for someone to change the value in the combobox using DOM manipulation and bet on sums he's not supposed to have. Always check that the input you're getting is what you expect it to be!
I'm writing an algorithm in PHP to solve a given Sudoku puzzle. I've set up a somewhat object-oriented implementation with two classes: a Square class for each individual tile on the 9x9 board, and a Sudoku class, which has a matrix of Squares to represent the board.
The implementation of the algorithm I'm using is a sort of triple-tier approach. The first step, which will solve only the most basic puzzles (but is the most efficient), is to fill in any squares which can only take a single value based on the board's initial setup, and to adjust the constraints accordingly on the rest of the unsolved squares.
Usually, this process of "constant propagation" doesn't solve the board entirely, but it does solve a sizable chunk. The second tier will then kick in. This parses each unit (or 9 squares which must all have unique number assignments, e.g. a row or column) for the "possible" values of each unsolved square. This list of possible values is represented as a string in the Square class:
class Square {
private $name; // 00, 01, 02, ... , 86, 87, 88
private $peers; // All squares in same row, col, and box
private $number; // Assigned value (0 if not assigned)
private $possibles; // String of possible numbers (1-9)
public function __construct($name, $p = 0) {
$this->name = $name;
$this->setNumber($p);
if ($p == 0) {
$this->possibles = "123456789";
}
}
// ... other functions
Given a whole array of unsolved squares in a unit (as described in the second tier above), the second tier will concatenate all the strings of "possibles" into a single string. It will then search through that single string for any unique character values - values which do not repeat themselves. This will indicate that, within the unit of squares, there is only one square that can take on that particular value.
My question is: for implementing this second tier, how can I parse this string of all the possible values in a unit and easily detect the unique value(s)? I know I could create an array where each index is represented by the numbers 1-9, and I could increment the value at the corresponding index by 1 for each possible-value of that number that I find, then scan the array again for any values of 1, but this seems extremely inefficient, requiring two linear scans of an array for each unit, and in a Sudoku puzzle there are 27 units.
This is somewhat like what you have already ruled out as "extremely inefficient", but with builtin functions so it might be quite efficient:
$all_possibilities = "1234567891234";
$unique = array();
foreach (count_chars($all_possibilities, 1) as $c => $occurrences) {
if ($occurrences == 1)
$unique[] = chr($c);
}
print join("", $unique) . "\n";
Prints: "56789"
Consider using a binary number to represent your "possibles" instead, because binary operations like AND, OR, XOR tend to be much faster than string operations.
E.g. if "2" and "3" are possible for a square, use the binary number 000000110 to represent the possibilities for that square.
Here's how you could find uniques:
$seenonce = 0;
$seenmore = 0;
foreach(all_possibles_for_this_unit as $possibles) {
$seenmore |= ($possibles & $seenonce);
$seenonce |= $possibles;
}
$seenonce ^= $seenmore;
if ($seenonce) {
//something was seen once - now it must be located
}
I'm not sure if this method will actually work faster but it's worth looking into.
function singletonsInString($instring) {
$results = array();
for($i = 1; $i < 10; $i++) {
$first_pos = strpos($instring, str($i));
$last_pos = strrpos($instring, str($i));
if ( $first_pos !== FALSE and $first_pos == $last_pos )
$results[] = $i;
}
return $results;
}
That'll give you every singleton. Get the first and last positions of a number in that array, and if they match and aren't both FALSE (strict comparison in case there's a singleton right at the start) then there's only one such number in that array.
If you're super super worried about speed here, you can probably replace the interior of that loop with
$istr = str($i);
if ( ($first = strpos($instring, $istr)) !== FALSE
and $first == $strrpos($instring, $istr) ) $results[] = $i;
for a minimum number of computations. Well, assuming PHP's native strpos is the best way to go about these things, which as far as I know is not unreasonable.
The last time I fooled with Sudoku solving, I had a third class called "Run". A Run instance is created for each row, col and 3x3 square. Every square has three runs associated with it. The Run class contains the set of numbers not yet placed within the run. Solving the board then involves intersecting the sets at each square iteratively. This takes care of 80% of most medium boards and 60% of most hard boards. Once you've gone through the whole board with no changes, you can move on to higher level logic. Each time your higher level logic fills a square, you run through your squares again.
The nice thing about this setup is you can easily add variants to the solver. Say you use the variant where the two diagonals are also unique. You just add a 4th run to those 18 squares.
What I would do, is actually use binary bits for storing actual values as another answer suggested. That allows to do efficient checks and in general might lend Sudoku itself to more mathematical(=efficient and shorter) solution (just my impression, I have not researched this).
Basically, you represent the numbers in squares not with digits, but with powers of 2
"1" = 2^0 = 1 = 000000001
"2" = 2^1 = 2 = 000000010
"3" = 2^2 = 4 = 000000100
"4" = 2^3 = 8 = 000001000
... etc up to
"9" = 2^8 = 256= 100000000
this way, you can simply add contents' of single squares to find out what numbers are missing in a 3x3 or a row or any other subset of sudoku, like this:
// shows the possibles for 3x3 square number 1 (00-22)
$sum=0;
for ($i=0; $i< 3; $i++)
for ($j=0; $j < 3; $j++)
$sum += $square["${i}${j}"]->number
$possibles = $sum ^ 511 // ^ stands for bitwise XOR and 511 is binary 11111111
now the $possibles contains "1" in bit positions of digits that are possible in this square and you can do bitwise operations with the results for other squares to match them together, like this:
eg. let's say:
$possibles1 = 146 // is binary 100100101,
//indicating that this row or 3x3 square has place for "9", "6", "3" and "1"
$possibles2 = 7 // is binary 000000111, indicating it has place for "3", "2" and "1".
// so:
$possibles1 & $possibles2
// bitwise AND, will show binary 101 saying that "3" and "1" is unfilled in both bloces
$possibles1 | $possibles2
// bitwise OR will give that in total it is possible to use "9", "6", "3", "2" and "1" in those two squares together
Here is a way using only PHP built-in functions which should be pretty fast.
function getUniques($sNumbers)
{
return join(array_keys(array_count_values(str_split($sNumbers)),1));
}
echo getUniques("1234567891234"); // return 56789;