<?php
class Gen
{
private $mask; // bitmask all combinations
private $firstOffset; // what first range starts from
private $secondOffset; // what second range starts from
private $firstWidth; // the width of first range
private $secondWidth; // the width of second range
private $permutations; // number of permutations
private $used = 0; // number of permutations used
public function __construct(array $range1, array $range2)
{
if( ! count($range1) || ! count($range2) )
throw new Exception("Both ranges must contain at least one element");
// I support both array(1, 2, 3, 4) and array(1, 4) as a range
$this->firstOffset = $range1[0];
$this->secondOffset = $range2[0];
$firstEnd = array_pop($range1);
$secondEnd = array_pop($range2);
if( $firstEnd < $this->firstOffset ||
$secondEnd < $this->secondOffset )
throw new Exception('End lower than start in at least one range.' .
"Given [{$this->firstOffset}, $firstEnd] " .
"and [{this->secondOffset}, $secondEnd]");
$this->firstWidth = 1 + $firstEnd - $this->firstOffset;
$this->secondWidth = 1 + $secondEnd - $this->secondOffset;
$this->permutations = $this->firstWidth*$this->secondWidth; // with no overlap
$this->reset();
}
public function next()
{
if( $this->used != $this->permutations ) {
$random_permutation = rand(0,$this->permutations);
if( ($found = gmp_scan0($this->mask, $random_permutation)) == $this->permutations )
$found = gmp_scan0($this->mask, 0);
$this->used++;
gmp_setbit($this->mask, $found);
$first = $found % $this->firstWidth + $this->firstOffset;
$second = floor($found / $this->firstWidth) + $this->secondOffset;
return array( $first, $second);
}
throw new Exception("No more pairs available");
}
public function reset()
{
$this->mask = gmp_init(0);
$this->used = 0;
// Make simple variables of
// so that the original ranges
// are [$s1, $e1] and [$s2, $e2]
$s1 = $this->firstOffset;
$s2 = $this->secondOffset;
$e1 = $s1 + $this->firstWidth - 1;
$e2 = $s2 + $this->secondWidth - 1;
if( $s1 <= $e2 && $s2 <= $e1 ) { // if the rhe ranges overlap
$is = max($s1, $s2); // get the start of intersect
$ie = min($e1, $e2); // get the end of intersect
$this->used += 1 + $ie - $is; // the count of emelemts in common
$i1end = $ie - $s1 + 1; // calculate the end element for first range
for( $i1 = $is - $s1, // initialize range1 by reducing by s1
$i2 = $is - $s2; // initialize range2 by reducing by s2
$i1 < $i1end; // as long as $i1 is lower than the calculates stop
$i1++, $i2++) { // increment both indexes by one for next iteration
// body of for loop sets bitmask where both
// would get the same value when offset is added
gmp_setbit( $this->mask, $i1 + $i2 * $this->firstWidth );
}
}
}
}
//$g = new Gen(range(1,6), range(1,10));
//$g = new Gen(array(3,4), array(5));
$g = new Gen(array(1,6), array(3291,83901));
try {
while(1) {
print implode(", ", $g->next()) . "\n";
}
} catch ( Exception $e ) {
print "Done!\n";
}
<?php
class Gen
{
private $mask; // bitmask all combinations
private $firstOffset; // what first range starts from
private $secondOffset; // what second range starts from
private $firstWidth; // the width of first range
private $secondWidth; // the width of second range
private $permutations; // number of permutations
private $used = 0; // number of permutations used
public function __construct(array $range1, array $range2)
{
if( ! count($range1) || ! count($range2) )
throw new Exception("Both ranges must contain at least one element");
// I support both array(1, 2, 3, 4) and array(1, 4) as a range
$this->firstOffset = $range1[0];
$this->secondOffset = $range2[0];
$firstEnd = array_pop($range1);
$secondEnd = array_pop($range2);
if( $firstEnd < $this->firstOffset ||
$secondEnd < $this->secondOffset )
throw new Exception('End lower than start in at least one range.' .
"Given [{$this->firstOffset}, $firstEnd] " .
"and [{this->secondOffset}, $secondEnd]");
$this->firstWidth = 1 + $firstEnd - $this->firstOffset;
$this->secondWidth = 1 + $secondEnd - $this->secondOffset;
$this->permutations = $this->firstWidth*$this->secondWidth; // with no overlap
$this->reset();
}
public function next()
{
if( $this->used != $this->permutations ) {
$random_permutation = rand(0,$this->permutations);
if( ($found = gmp_scan0($this->mask, $random_permutation)) == $this->permutations )
$found = gmp_scan0($this->mask, 0);
$this->used++;
gmp_setbit($this->mask, $found);
$first = $found % $this->firstWidth + $this->firstOffset;
$second = floor($found / $this->firstWidth) + $this->secondOffset;
return array( $first, $second);
}
throw new Exception("No more pairs available");
}
public function reset()
{
$this->mask = gmp_init(0);
$this->used = 0;
$s1 = $this->firstOffset;
$s2 = $this->secondOffset;
$e1 = $s1 + $this->firstWidth - 1;
$e2 = $s2 + $this->secondWidth - 1;
if( $s1 <= $e2 && $s2 <= $e1 ) {
$is = max($s1, $s2);
$ie = min($e1, $e2);
$this->used += 1 + $ie - $is;
$i1end = $ie - $s1 + 1;
for( $i1 = $is - $s1,
$i2 = $is - $s2;
$i1 < $i1end;
$i1++, $i2++) {
gmp_setbit( $this->mask, $i1 + $i2 * $this->firstWidth );
}
}
}
}
//$g = new Gen(range(1,6), range(1,10));
//$g = new Gen(array(3,4), array(5));
$g = new Gen(array(1,6), array(3291,83901));
try {
while(1) {
print implode(", ", $g->next()) . "\n";
}
} catch ( Exception $e ) {
print "Done!\n";
}
<?php
class Gen
{
private $mask; // bitmask all combinations
private $firstOffset; // what first range starts from
private $secondOffset; // what second range starts from
private $firstWidth; // the width of first range
private $secondWidth; // the width of second range
private $permutations; // number of permutations
private $used = 0; // number of permutations used
public function __construct(array $range1, array $range2)
{
if( ! count($range1) || ! count($range2) )
throw new Exception("Both ranges must contain at least one element");
// I support both array(1, 2, 3, 4) and array(1, 4) as a range
$this->firstOffset = $range1[0];
$this->secondOffset = $range2[0];
$firstEnd = array_pop($range1);
$secondEnd = array_pop($range2);
if( $firstEnd < $this->firstOffset ||
$secondEnd < $this->secondOffset )
throw new Exception('End lower than start in at least one range.' .
"Given [{$this->firstOffset}, $firstEnd] " .
"and [{this->secondOffset}, $secondEnd]");
$this->firstWidth = 1 + $firstEnd - $this->firstOffset;
$this->secondWidth = 1 + $secondEnd - $this->secondOffset;
$this->permutations = $this->firstWidth*$this->secondWidth; // with no overlap
$this->reset();
}
public function next()
{
if( $this->used != $this->permutations ) {
$random_permutation = rand(0,$this->permutations);
if( ($found = gmp_scan0($this->mask, $random_permutation)) == $this->permutations )
$found = gmp_scan0($this->mask, 0);
$this->used++;
gmp_setbit($this->mask, $found);
$first = $found % $this->firstWidth + $this->firstOffset;
$second = floor($found / $this->firstWidth) + $this->secondOffset;
return array( $first, $second);
}
throw new Exception("No more pairs available");
}
public function reset()
{
$this->mask = gmp_init(0);
$this->used = 0;
// Make simple variables of
// so that the original ranges
// are [$s1, $e1] and [$s2, $e2]
$s1 = $this->firstOffset;
$s2 = $this->secondOffset;
$e1 = $s1 + $this->firstWidth - 1;
$e2 = $s2 + $this->secondWidth - 1;
if( $s1 <= $e2 && $s2 <= $e1 ) { // if the rhe ranges overlap
$is = max($s1, $s2); // get the start of intersect
$ie = min($e1, $e2); // get the end of intersect
$this->used += 1 + $ie - $is; // the count of emelemts in common
$i1end = $ie - $s1 + 1; // calculate the end element for first range
for( $i1 = $is - $s1, // initialize range1 by reducing by s1
$i2 = $is - $s2; // initialize range2 by reducing by s2
$i1 < $i1end; // as long as $i1 is lower than the calculates stop
$i1++, $i2++) { // increment both indexes by one for next iteration
// body of for loop sets bitmask where both
// would get the same value when offset is added
gmp_setbit( $this->mask, $i1 + $i2 * $this->firstWidth );
}
}
}
}
//$g = new Gen(range(1,6), range(1,10));
//$g = new Gen(array(3,4), array(5));
$g = new Gen(array(1,6), array(3291,83901));
try {
while(1) {
print implode(", ", $g->next()) . "\n";
}
} catch ( Exception $e ) {
print "Done!\n";
}
This is based on @MichaelT comments and code. I started with this before he posted it but i have been peeking after he posted his code so it's not entirely original work.
I reserve where the numbers in both ranges are the same and i add the offset so that it's more aligned with the original spec.
<?php
class Gen
{
private $mask; // bitmask all combinations
private $firstOffset; // what first range starts from
private $secondOffset; // what second range starts from
private $firstWidth; // the width of first range
private $secondWidth; // the width of second range
private $permutations; // number of permutations
private $used = 0; // number of permutations used
public function __construct(array $range1, array $range2)
{
if( ! count($range1) || ! count($range2) )
throw new Exception("Both ranges must contain at least one element");
// I support both array(1, 2, 3, 4) and array(1, 4) as a range
$this->firstOffset = $range1[0];
$this->secondOffset = $range2[0];
$firstEnd = array_pop($range1);
$secondEnd = array_pop($range2);
if( $firstEnd < $this->firstOffset ||
$secondEnd < $this->secondOffset )
throw new Exception('End lower than start in at least one range.' .
"Given [{$this->firstOffset}, $firstEnd] " .
"and [{this->secondOffset}, $secondEnd]");
$this->firstWidth = 1 + $firstEnd - $this->firstOffset;
$this->secondWidth = 1 + $secondEnd - $this->secondOffset;
$this->permutations = $this->firstWidth*$this->secondWidth; // with no overlap
$this->reset();
}
public function next()
{
if( $this->used != $this->permutations ) {
$random_permutation = rand(0,$this->permutations);
if( ($found = gmp_scan0($this->mask, $random_permutation)) == $this->permutations )
$found = gmp_scan0($this->mask, 0);
$this->used++;
gmp_setbit($this->mask, $found);
$first = $found % $this->firstWidth + $this->firstOffset;
$second = floor($found / $this->firstWidth) + $this->secondOffset;
return array( $first, $second);
}
throw new Exception("No more pairs available");
}
public function reset()
{
$this->mask = gmp_init(0);
$this->used = 0;
$s1 = $this->firstOffset;
$s2 = $this->secondOffset;
$e1 = $s1 + $this->firstWidth - 1;
$e2 = $s2 + $this->secondWidth - 1;
if( $s1 <= $e2 && $s2 <= $e1 ) {
$is = max($s1, $s2);
$ie = min($e1, $e2);
$this->used += 1 + $ie - $is;
$i1end = $ie - $s1 + 1;
for( $i1 = $is - $s1,
$i2 = $is - $s2;
$i1 < $i1end;
$i1++, $i2++) {
gmp_setbit( $this->mask, $i1 + $i2 * $this->firstWidth );
}
}
}
}
//$g = new Gen(range(1,6), range(1,10));
//$g = new Gen(array(3,4), array(5));
$g = new Gen(array(1,6), array(3291,83901));
try {
while(1) {
print implode(", ", $g->next()) . "\n";
}
} catch ( Exception $e ) {
print "Done!\n";
}
lang-php