All combinations without repetitions with specific cardinality - php

I have an array:
[a, b, c, d, e, f, ... , z]
and I would generate the set of all possible subarrays, withuout repetition, whose cardinality is between X and Y.
Let's assume php:
$array = array(1, 2, 3, 4, 5, 6, 7, 8);
$results = myFunction($array, 3, 5);
My function should returns something like:
array(
array(1, 2, 3),
array(1, 2, 4),
...
array(4, 5, 6, 7, 8),
);
My attempt was to count in binary, from 0 to 2^n (where n is the cardinality of the set) and if the number of 1s is between X and Y, add the array made of 1s element to the result set.
Eg.
8 = 0000 0111 => add (6,7,8) to result
9 = 0000 1000 => no
10 = 0000 1001 => no
...
but it's very ugly!
Any better algorithm?
I'm using php, but feel free to use whatever language you like.

A pretty straightforward solution (requires generators, I think, it's php 5.5+)
// generate combinations of size $m
function comb($m, $a) {
if (!$m) {
yield [];
return;
}
if (!$a) {
return;
}
$h = $a[0];
$t = array_slice($a, 1);
foreach(comb($m - 1, $t) as $c)
yield array_merge([$h], $c);
foreach(comb($m, $t) as $c)
yield $c;
}
and then
$a = ['a','b','c','d','e','f', 'g'];
foreach(range(3, 5) as $n)
foreach(comb($n, $a) as $c)
echo join(' ', $c), "\n";

Related

Return the smallest positive integer

function solution($A);
that, given an array A of N integers, returns the smallest positive integer (greater than 0) that does not occur in A.
For example, given A = [1, 3, 6, 4, 1, 2], the function should return 5.
Given A = [1, 2, 3], the function should return 4.
Given A = [−1, −3], the function should return 1.
Write an efficient algorithm for the following assumptions:
N is an integer within the range [1..100,000];
each element of array A is an integer within the range [−1,000,000..1,000,000].
Below is my attempt:
function solution($A) {
// write your code in PHP7.0
$n=1;
while($n > 0 && $n <= 1000000)
$n ++;
echo $A=$n+1;
}
echo solution;
?>```
Try this, no loop required:
<?php
function solution($set) {
$diff = array_diff(range(1, max($set)), $set);
sort($diff);
return !isset($diff[0]) ? max($set) + 1 : ($diff[0] < 1 ? 1 : $diff[0]);
}
echo solution([39, 68, 47, 2, 19, 64]); // 1
echo solution([1, 3, 6, 4, 1, 2]); // 5
echo solution([1, 2, 3]); // 4
echo solution([-1, -3]); // 1
https://3v4l.org/h28LZ
Here's a one-liner to either impress your professor or get kicked out of the class for not following instructions:
php > function solution(array $A) { return max(array(1,min(array_diff(range(1,100000),$A)))); }
php > echo solution([39, 68, 47, 2, 19, 64]);
1
php > echo solution([1,3,6,4,1,2]);
5
php > echo solution([-1,-3]);
1
php > echo solution([1,2,3]);
4
It generates array N (1-1,000,000) and runs array_diff against your input A, and returns either the lowest result from that comparison or 1 if it's less than or equal to 0.
function solution($A) {
$smallest = 1;
while(in_array($smallest, $A)){
$smallest++;
}
return $smallest;
}
Above is the smallest code but has O(N^2) Complexity with 66% success
function solution($A) {
$flipped = array_flip($A);
$smallest = 1;
while (isset($flipped[$smallest])){
$smallest++;
}
return $smallest;
}
Detected time complexity:
O(N) or O(N * log(N)) with 100% success rate

Generate random "pattern-lock" sequence of digits

Today my friend raised a challenge that I still can't solve: "Generate a random digit sequence in PHP"
The digits are arranged as dial-pad/pattern-lock that consist 1-9 keys in 3 rows and 3 columns:
---------------------------
| |
| 1 2 3 |
| |
| 4 5 6 |
| |
| 7 8 9 |
| |
---------------------------
Now, given a length, we have to generate a random, non-repeating sequence of digits of the provided length, using these criteria:
A generated sequence should follow a specific direction/pattern going only via neighboring digits (possibly diagonally), for example (length:8), 12569874:
1 🡪 2
🡫
4 5 🡪 6
🡩 🡫
7 🡨 8 🡨 9
Digits from the first row should never be followed by a digit from the third row, and vice-versa. The same goes for columns. For example a 1 cannot be followed by a 8, and a 6 cannot be followed by a 4.
can guess more criteria can easily from android pattern-lock system
Here are some example generated sequences for length 9: 12369874/5, 142536987, etc, and for length = 6: 987532, etc
I tried to do this with rand():
$chars = "123456789";
$length = 9;
$clen = strlen( $chars )-1;
$id = '';
for ($i = 0; $i < $length; $i++) {
$id .= $chars[mt_rand(0,$clen)];
}
return ($id);
but, still no luck...
How can I solve this question?
has some limitations but that's for you to work out. I only deal with headaches when I get paid :).
<pre>
<?php
// Keypad
$grid = [
['1', '2', '3'],
['4', '5', '6'],
['7', '8', '9'],
];
// Sequence Target Length
$target_length = 5;
// Place to store the Keypad sequence
$points = [];
// Starting Point
$x = rand(0, 2);
$y = rand(0, 2);
// Run through the process until we have the sequence at the desired length
while (count($points) < $target_length):
// Check if the grid keypad entry has been used
if ($grid[$x][$y]):
// Hasn't been used, so stire it
$points[] = $grid[$x][$y];
// Mark it used
$grid[$x][$y] = NULL;
endif;
// Sanity Check, imagine if you will,.... target length of 9, and you hit 6 5 2 1, You'll vault off into the twilight zone without this
if ((!$grid[$x + 1][$y]) && (!$grid[$x][$y + 1]) && (!$grid[$x - 1][$y]) && (!$grid[$x][$y - 1])):
// We have no where to go
break;
endif;
// Start looking for possible values
do {
$test_x = $x;
$test_y = $y;
$dir = rand(0, 3);
switch ($dir):
case (0):
$test_y--; // Up
break;
case (1):
$test_x++; // Right
break;
case (2):
$test_y++; // Down
break;
case (3):
$test_x--; // Left
break;
endswitch;
// Optional Gibberish
echo "Moving from {$x}, {$y} to {$test_x}, {$test_y} --> " . (($grid[$test_x][$test_y] === NULL) ? 'FAILED' : 'OK!') . '<br>';
// Keep going until we find a valid direction
} while ($grid[$test_x][$test_y] === NULL);
// assign the new coords
$x = $test_x;
$y = $test_y;
// repeat
endwhile;
// report
echo implode('-', $points) . "\n";
?>
</pre>
Here is a solution that will apply these rules:
a path can only step to neighboring cells, i.e. that are adjacent, including diagonally
a path cannot contain the same cell twice
The following algorithm uses recursion for every digit that is added to the sequence. Whenever the sequence gets "stuck", backtracking happens, and an alternative path is tried. Backtracking continues if no more alternatives are available.
It is guaranteed that a path of the given length is returned, provided the given length is between 1 and 9:
function randomSequence($len) {
if ($len < 1 || $len > 9) return []; // No results
$row = [null, 1, 1, 1, 2, 2, 2, 3, 3, 3];
$col = [null, 1, 2, 3, 1, 2, 3, 1, 2, 3];
$neighbors = [[], [2, 4, 5], [1, 4, 5, 6, 3], [2, 5, 6],
[1, 2, 5, 7, 8], [1, 2, 3, 4, 6, 7, 8, 9], [2, 3, 5, 8, 9],
[4, 5, 8], [4, 5, 6, 7, 9], [5, 6, 8]];
// Shuffle the neighbor lists to implement the randomness:
foreach ($neighbors as &$nodes) shuffle($nodes);
$recurse = function ($seq) use (&$len, &$row, &$col, &$neighbors, &$recurse) {
if (count($seq) >= $len) return $seq; // found solution
$last = end($seq);
echo "try " . json_encode(array_keys($seq)) . "\n";
foreach ($neighbors[$last] as $next) {
if (isset($seq[$next])) continue; // Skip if digit already used
$result = $recurse($seq + [$next => $next]);
if (is_array($result)) return $result;
}
};
$choice = rand(1, 9);
return array_keys($recurse([$choice => $choice]));
}
echo "result: " . json_encode(randomSequence(9)) . "\n";
See it run on repl.it
Here's an example in pseudocode for a matrix that looks like this:
1 2
3 4
# Get which other numbers are "legal moves" from each number.
adjacency = {
1: [2, 3],
2: [1, 4],
3: [1, 4],
4: [2, 3]
}
# Get the length of code required.
n = 8
# Start at a random position;
pos = rand(keys(adjacency))
result = []
while (n > 0)
n -= 1
newpos = rand(adjacency[pos])
result[] = newpos
pos = newpos
print(result.join(', '))
If your matrix is going to be large or is going to vary you might want to write some code to generate adjaceny rather than hardcoding it.

PHP: Number of consecutive elements in array [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I have been working on one problem:
Find the largest group of consecutive numbers in an array.
Say we have an array [5, 43, 4, 56, 3, 2, 44, 57, 58, 1], the biggest group of consecutive numbers in this array is 5 (1, 2, 3, 4, and 5).
The solution algorithm must be time complexity of O(n).
I have solved this with the following ruby code but I am having trouble porting it to PHP as the solution requires.
arr = [8, 13, 14, 10, 6, 7, 8, 14, 5, 3, 5, 2, 6, 7, 4]
result = []
stage = []
for i in arr:
if len(stage) > 0 and i != stage[-1]+1:
if len(stage) > 1:
result.append(stage)
stage = []
stage.append(i)
print result
$a = [8, 13, 14, 10, 6, 7, 8, 14, 5, 3, 5, 2, 6, 7, 4];
$res = [];
$stage = [];
foreach($a as $i) {
if(count($stage) > 0 && $i != $stage[count($stage)-1]+1) {
if(count($stage) > 1) {
$res[] = $stage;
}
$stage = [];
}
$stage[] = $i;
}
print_r($res);
It's not O(n) but you can try this:
// Define array
$array = array(5,8,3,2,10,11,15,13,12,1,4,5,16);
// Sorting
asort($array);
$previous = null;
$result = array();
$consecutiveArray = array();
// Slice array by consecutive sequences
foreach($array as $number) {
if ($number == $previous + 1) {
$consecutiveArray[] = $number;
} else {
$result[] = $consecutiveArray;
$consecutiveArray = array($number);
}
$previous = $number;
}
$result[] = $consecutiveArray;
// Get length of each sub array
$count = array_map('count', $result);
You can get max length by max($count).
This solution gives you following array:
array(
0 => array(1,2,3,4,5)
1 => array(5)
2 => array(8)
3 => array(10,11,12,13)
4 => array(15,16)
Here is a python (my PHP is not too good) that does what your description asks, in o(n) if your sequence is decreasing:
lists = dict()
for i in val:
if i in lists:
continue
a = {i}
if (i + 1) in lists:
b = lists[i+1]
b.update(a)
a = b
if (i - 1) in lists:
b = lists[i-1]
# this messes up the complexity
for k in b:
lists[k] = a
a.update(b)
lists[i] = a
The idea is that lists maintain a dict of sets indexed on all the elements in the list. Whenever you encounter a new element, the previous and next sets are merged, if present.
The update operation is technically o(n), but it is not compounded by the external loop, as there can only be n insertion into sets by merging. The overall is o(n)
If the sequence is not sorted, the merge of the +1 and -1 sets gives a not-so-good complexity.

Finding prime and composite numbers in a string

I have the following randomly generated string:
$text = 's$bp4q1hsq3#g88nsjm5hr#i9#3078e2m';
What I need is to take all integers from it and classify them as either prime or composite numbers and estimate their sum. All numbers should be assumed that are one digit so this shortens the values to only four per each group:
$primes = array(2, 3, 5, 7);
$composites = array(4, 6, 8, 9);
This means: Primes: 5, 3, 3, 2 = 13 and Composites: 4, 8, 8, 9, 8 = 37 as duplicate numbers also count.
I have tried grabbing the numbers like so:
$asArray = str_split($text);
foreach ($asArray as $element) {
if (is_int($element)) {
echo $element;
}
}
But it seems to end up in a blank page. So my question is how can I find out the numbers in a string and then classify them as either prime or composite?
Here you have the sum of the primes and composites:
$text = 's$bp4q1hsq3#g88nsjm5hr#i9#3078e2m';
$primes = array(2, 3, 5, 7);
$sum_primes = $sum_composites = 0;
preg_match_all("/\d/", $text, $matches);
foreach($matches[0] as $number)
{
if (in_array($number, $primes))
$sum_primes += $number;
else
$sum_composites += $number;
}
echo "Sum of primes: ".$sum_primes."\n";
echo "Sum of composites: ".$sum_composites."\n";
It would print,
Sum of primes: 20
Sum of composites: 38

php array generation challenge

I need to randomly generate an two-dimensional n by n array. In this example, n = 10. The array should have this structure. One example:
$igra[]=array(0,1,2,3,4,5,6,7,8,9);
$igra[]=array(6,9,1,5,0,2,7,3,4,8);
$igra[]=array(2,5....................
$igra[]=array(1,7.....................
$igra[]=array(5,4...................
$igra[]=array(4,2...................
$igra[]=array(9,0.....................
$igra[]=array(8,3.....................
$igra[]=array(7,6....................
$igra[]=array(3,8....................
where
`$igra[x][z]!=$igra[y][z]` (x={0,9},y={0,9});
as you see it's like a matrix of numbers each row of it and column also consist from numbers 0-9, and there is never one number two times in each row or in each column.
how to generate such an array, and each time randomly.
Okay, so here's my version:
$n = 10;
$v1 = range(0, $n-1);
$v2 = range(0, $n-1);
shuffle($v1);
shuffle($v2);
foreach ($v1 as $x => $value)
foreach ($v2 as $y)
$array[$y][$x] = $value++ % $n;
This should be a really fast algorithm, because it involves only generating two random arrays and doesn't involve any swapping at all. It should be random, too, but I cannot prove it. (At least I don't know how to prove something like this.)
This is an optimized version of a very simple algorithm:
First a non-random matrix is created this way (imagine we want only 5*5, not 10*10):
0 1 2 3 4
1 2 3 4 0
2 3 4 0 1
3 4 0 1 2
4 0 1 2 3
In this matrix we now randomly swap columns. As we don't change the columns themselves your rules still are obeyed. Then we randomly swap rows.
Now, as you can see the above algorithm doesn't swap anything and it doesn't generate the above matrix either. That's because it generates the cols and rows to swap in advance ($v1 and $v2) and then directly writes to the correct position in the resulting array.
Edit: Just did some benchmarking: For $n = 500 it takes 0.3 seconds.
Edit2: After replacing the for loops with foreach loops it only takes 0.2 seconds.
This is what I did. Made a valid matrix (2d array) that isn't random. So starting out, row 0 is 0-9, row 1 is 1-0 (ie: 1,2,3...8,9,0), row 2 is 2-1 (2,3...9,0,1)...row 8 is 8-7...etc. Then shuffle that array to randomize the rows and perform a simple column swap to randomize the columns. Should get back exactly what you want. Try this:
<?php
//simple function to show the matrix in a table.
function show($matrix){
echo '<table border=1 cellspacing=0 cellpadding=5 style="float: left; margin-right:20px;">';
foreach($matrix as $m){
echo '<tr>';
foreach($m as $n){
echo '<td>'.$n.'</td>';
}
echo '</tr>';
}
echo '</table>';
}
//empty array to store the matrix
$matrix = array();
//this is what keeps the current number to put into matrix
$cnt = 0;
//create the simple matrix
for($i=0;$i<=9;$i++){
for($j=0;$j<=9;$j++){
$matrix[$i][$j] = $cnt % 10;
$cnt++;
}
$cnt++;
}
//display valid simple matrix
show($matrix);
//shuffle the rows in matrix to make it random
shuffle($matrix);
//display matrix with shuffled rows.
show($matrix);
//swap the columns in matrix to make it more random.
for($i=0;$i<=9;$i++){
//pick a random column
$r = mt_rand(0, 9);
//now loop through each row and swap the columns $i with $r
for($j=0;$j<=9;$j++){
//store the old column value in another var
$old = $matrix[$j][$i];
//swap the column on this row with the random one
$matrix[$j][$i] = $matrix[$j][$r];
$matrix[$j][$r] = $old;
}
}
//display final matrix with random rows and cols
show($matrix);
?>
In my solution, by not generating a random array and checking if it already exists, it should run much faster (especially if the array ever went above 0-9). When you get down to the last row, there is only one possible combination of numbers. You will be generating random arrays trying to find that one answer. It would be pretty much the same as picking a number from 1 to 10 and generating a random number until it hits the one you picked. It could be on the first try, but then again you could pick 1000 random numbers and never get the one you wanted.
Hmm.. I see you got some good answers already, but here's my version:
$n = 10;
$seed_row = range(0, $n - 1);
shuffle($seed_row);
$result = array();
for($x = 0; $x < $n; $x++)
{
$tmp_ar = array();
$rnd_start = $seed_row[$x];
for($y = $rnd_start; $y < ($n + $rnd_start); $y++)
{
if($y >= $n) $idx = $y - $n;
else $idx = $y;
$tmp_ar[] = $seed_row[$idx];
}
$result[] = $tmp_ar;
}
for($x = 0; $x < $n; $x++)
{
echo implode(', ', $result[$x]) . "<br/>\n";
}
sample output:
4, 3, 0, 2, 6, 5, 7, 1, 8, 9
0, 2, 6, 5, 7, 1, 8, 9, 4, 3
7, 1, 8, 9, 4, 3, 0, 2, 6, 5
2, 6, 5, 7, 1, 8, 9, 4, 3, 0
6, 5, 7, 1, 8, 9, 4, 3, 0, 2
9, 4, 3, 0, 2, 6, 5, 7, 1, 8
8, 9, 4, 3, 0, 2, 6, 5, 7, 1
5, 7, 1, 8, 9, 4, 3, 0, 2, 6
1, 8, 9, 4, 3, 0, 2, 6, 5, 7
3, 0, 2, 6, 5, 7, 1, 8, 9, 4
It creates a random random array as a starting point
Then it walks through the seed array taking each element as a starting point for itself to create a new base.

Categories