3

Hi I'm trying to build a function which will walk through all possible number sequences and pass the sequence through a function and if it return true stops.

Here is the markup:

function sequences($smallest, $biggest, $long, $func) {
   $seq = array(); // Will have $long values 
   /*
    * generates the sequence
   */

   if (call_user_func($functions[$func])) {
      return $seq;
   } else {
      //generate next sequence.
   }

} 

The generated sequence will have $long unique values between $smallest integer to $biggest integer and must be sorted example:

/* $long = 4; $smallest = 5, $biggest = 10;
 * 
 * 5,6,7,8
 * 5,6,7,9
 * 5,6,7,10
 * 5,6,8,9
 * 5,6,8,10
 * ...
 * 7,8,9,10
 *
 *
 * $long = 4; $smallest = 15, $biggest = 60;
 *
 * ...
 * 15,41,49,56
 * ...
 * 37,39,53,60
 * ...
 */

I haven’t been able to wrap my head around it, so far the only way I achieve that was to generate numbers randomly and than sorting the array each time. That's clearly not the best way.

Other programming languages will be great too (c++, C#, js, java).

NOTES

  • Its not an odometer duplicate numbers in the sequence are not allowed and the values of each index can be bigger than 9.
  • The function that tests the sequence against a some conditions and will return True or False it doesn't matter what it does the actual problem is generating the sequences one by one without duplicates.
Shlomi Hassid
  • 6,500
  • 3
  • 27
  • 48
  • 4
    Think about how the numbers change on an odometer. – Barmar Apr 30 '15 at 23:55
  • When does `etc...` stop? – scrowler Apr 30 '15 at 23:56
  • @scrowler when all possibilities are done or the function returns true – Shlomi Hassid Apr 30 '15 at 23:57
  • @ShlomiHassid when all possibilities are done meaning you reach `9,9,9,9` in that example? When would the function return true? Pretty vague... – scrowler Apr 30 '15 at 23:59
  • @scrowler - no sorry I wasn't clear enough duplicates are not allowed. the biggest in this case will be : 7,8,9,10 – Shlomi Hassid May 01 '15 at 00:02
  • @scrowler it doesn't matter what the test function will do - when the right sequence is generated it will return true. the actual problem is to generate the sequences. – Shlomi Hassid May 01 '15 at 00:05
  • 2
    @Barmar its not an odometer duplicate numbers in the sequence are not allowed and the values of each index can be bigger than 9 – Shlomi Hassid May 01 '15 at 00:12
  • @Barmar really??? it has nothing to do with the question you found and marked as duplicate. – Shlomi Hassid May 01 '15 at 00:25
  • Except for the fact that the question asks for combinations from `1 .. 99`, it's the same problem. Just replace the range `1 .. 99` with `$smallest .. $biggest`. – Barmar May 01 '15 at 00:28
  • @barmar No mate it accepts any order - and his after random sequences most of the answers uses shuffling and random numbers in their answers please reopen. – Shlomi Hassid May 01 '15 at 00:32
  • OK, I've reopened. But I'm sure with some more searching someone should be able to find a duplicate. This is hardly a unique problem. – Barmar May 01 '15 at 00:33
  • @barmar - thank you, I tried my own for hours and searched a lot nothing exact came up mostly random sets which is not a big problem. – Shlomi Hassid May 01 '15 at 00:34
  • This sounds like a homework assignment on recursion. If that's true then you better use recursion in your answer. – KnightHawk May 01 '15 at 02:16
  • @knightHawk - I love when every time its an actual programming and problem solving question the first response is "homework..." – Shlomi Hassid May 01 '15 at 02:21
  • Recursion might be what you need though. I was going to describe a more brute force method with nested loops but it really reminds me of a homework assignment on recursion. Recursion isn't my strong suit which is why I didn't give a real answer. – KnightHawk May 01 '15 at 03:22

1 Answers1

1

This was a fun challenge to generate the sequences as you specified.

This code below should do what you want (I think). Or at least you should be able to modify it to suit your needs. I wasn't exactly sure if you wanted the sequences() function to return only the first sequence for which the test function $functions[$func] returns true, or all the sequences so far. In this example only the first "match" is returned (or null if no match was found).

This code requires PHP 5.5+ as it uses a generator function (and also short array syntax available in PHP 5.4+). I tested this on PHP 5.5.12 and it seems to work as intended. The code can probably be modified to work on older PHP versions if needed (just avoid using generators/yields). Actually this is the first time I've written a PHP generator function.

sequenceGenerator() is a recursive generator function that you can iterate over using foreach.

I also wrote an echoSequences() function for testing the sequence generation which just outputs all of the generated sequences in order using echo.

function sequenceGenerator(array $items, $long = null, $level = 1, $path = null) {
    $itemCount = count($items);
    if (empty($long)) $long = $itemCount;
    if ($path == null) $path = [];

    if ($itemCount > 1) {
        foreach ($items as $item) {
            $subPath = $path;
            $subPath[] = $item;
            if ($level == $long) {
                yield $subPath;
                continue;
            }

            if (count($subPath) + count($items) > $long) {
                $items = array_values(array_diff($items, [$item]));
                $iteration = sequenceGenerator($items, $long, $level + 1, $subPath);
                foreach ($iteration as $value) yield $value;
            }
        }
    } elseif ($itemCount == 1) {
        $path[] = $items[0];
        yield $path;
    }
}

// Function for testing sequence generation
function echoSequences($smallest, $biggest, $long) {
    $items = range($smallest, $biggest);
    foreach (sequenceGenerator($items, $long) as $sequence) {
        echo implode(',', $sequence)."<br>\n";
    }
}

function sequences($smallest, $biggest, $long, $func) {
    global $functions;
    $items = range($smallest, $biggest);
    foreach (sequenceGenerator($items, $long) as $sequence) {
        if (call_user_func($functions[$func], $sequence)) {
            return $sequence;
        }
    }
    return null; // Return null when $func didn't return true for any sequence
}

//echoSequences(5, 10, 4); // Test sequence generation

$functions = array(
    // This test function returns true only for the sequence [5,6,8,10]
    'testfunc' => function($sequence) { return ($sequence == [5,6,8,10]); }
);

$sequence = sequences(5, 10, 4, 'testfunc'); // Find the first sequence that 'testfunc' will return true for (or null)
if (!empty($sequence)) {
    echo 'Found match: '.implode(',', $sequence);
} else {
    echo 'Match not found';
}
Haprog
  • 793
  • 1
  • 9
  • 21
  • I would suggest modfying the code so that the "test function" is passed straight to the `sequences()` function as a `callable` instead of having to get it from a global variable as in this example. I just wanted to make the `sequences()` function look like your original code as much as possible so I ended up using `globals` in the function. – Haprog May 06 '15 at 17:03
  • And just out of curiosity it would be nice to know a bit more about what is a useful use case for this kind of sequence generation? What are you actually using this for (if you can say)? – Haprog May 06 '15 at 17:10
  • Amazing and interesting concept - finally someone who get it! I'll run some performance tests but it defiantly does what i'm after. great work! – Shlomi Hassid May 06 '15 at 19:51
  • Its a step in a data compression algorithm - I was doing that by generating random sequences and sorting them. It was doing the job but its so slow... – Shlomi Hassid May 06 '15 at 19:56
  • Let me know when if you find my code to be too slow. I can already think of at least few minor optimizations that could probably speed it up a little bit if you're generating huge amounts of sequences. – Haprog May 07 '15 at 07:24