Inventory sequence

From Rosetta Code
Inventory sequence is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

To build the inventory sequence, first take inventory of what numbers are already in the sequence, then, starting with 0 add the count of each number in turn to the end of the sequence. When you reach a number that has a count of 0, stop, add the 0 to the end of the sequence, and then restart the inventory from 0.


E.G.
Start taking inventory; how many 0s are there? 0. Add a 0 to the end of the sequence and restart the inventory. (0)
How many 0s are there? 1. Add a 1 to the end of the sequence. How many 1s are there? 1. Add a 1 to the end of the sequence. How many 2s are there? 0. Add a 0 to the end of the sequence and restart the inventory. (0 1 1 0)
and so on.


Task
  • Find and display the first 100 elements of the sequence.
  • Find and display the position and value of the first element greater than or equal to 1000.


Stretch
  • Find and display the position and value of the first element greater than or equal to 2000, 3000 ... 10,000.
  • Plot a graph of the first 10,000 elements of the sequence.


See also


jq

Works with both jq and gojq, the C and Go implementations of jq

... but note that gojq takes about 5 times longer (and requires much more memory) to complete the task.

The definition of `_nwise` can be omitted if using the C implementation of jq.

def _nwise($n):
  def n: if length <= $n then . else .[0:$n] , (.[$n:] | n) end;
  n;

# Emit the inventory sequence ad infinitum
def inventory_sequence:
  {num: 0,
   emit: 0,
   inventory: {} }
   | foreach range(0; infinite) as $n (.;
       .emit = (.inventory[.num|tostring] // 0) 
       | if .emit == 0 then .num = 0 else .num += 1 end
       | .inventory[.emit|tostring] += 1 )
   | .emit ;

# Report on the progress of an arbitrary sequence, indefinitely
# Emit [.next, $x, .n]
def probe(s; $gap):
  foreach s as $x ({n: 0, next: $gap};
    .n += 1
    | if $x >= .next then .emit = {next, $x, n} | .next += $gap
      else .emit = null
      end)
  | select(.emit).emit;

def lpad($len): tostring | ($len - length) as $l | (" " * $l)[:$l] + .;

def task($n):
  [limit($n; inventory_sequence)] | _nwise(10) | map(lpad(3)) | join(" ");

task(100),
"",
(limit(10; probe(inventory_sequence; 1000))
 | "First element >= \(.next) is \(.x) at index \(.n - 1)")
Output:
  0   1   1   0   2   2   2   0   3   2
  4   1   1   0   4   4   4   1   4   0
  5   5   4   1   6   2   1   0   6   7
  5   1   6   3   3   1   0   7   9   5
  3   6   4   4   2   0   8   9   6   4
  9   4   5   2   1   3   0   9  10   7
  5  10   6   6   3   1   4   2   0  10
 11   8   6  11   6   9   3   2   5   3
  2   0  11  11  10   8  11   7   9   4
  3   6   4   5   0  12  11  10   9  13

First element >= 1000 is 1001 at index 24255
First element >= 2000 is 2009 at index 43301
First element >= 3000 is 3001 at index 61708
First element >= 4000 is 4003 at index 81456
First element >= 5000 is 5021 at index 98704
First element >= 6000 is 6009 at index 121342
First element >= 7000 is 7035 at index 151756
First element >= 8000 is 8036 at index 168804
First element >= 9000 is 9014 at index 184428
First element >= 10000 is 10007 at index 201788

Python

''' rosettacode.org/wiki/Inventory_sequence '''
from collections import Counter
from matplotlib.pyplot import plot

def inventory_sequence(terms):
    ''' From the code by Branicky at oeis.org/A342585 '''
    num, alst, inventory = 0, [0], Counter([0])
    for n in range(2, terms+1):
        c = inventory[num]
        num = 0 if c == 0 else num + 1
        alst.append(c)
        inventory.update([c])
    return alst

biglist = inventory_sequence(201_790)
thresholds = [1000 * j for j in range(1, 11)]

for i, k in enumerate(biglist):
    if i < 100:
        print(f'{k:<4}', end='\n' if (i + 1) % 20 == 0 else '')
    elif k >= thresholds[0]:
        print(f'\nFirst element >= {thresholds.pop(0):5}: {k:5} in position {i:6}')
        if len(thresholds) == 0:
               break

plot(biglist[:10_000], linewidth=0.3)
Output:
0   1   1   0   2   2   2   0   3   2   4   1   1   0   4   4   4   1   4   0   
5   5   4   1   6   2   1   0   6   7   5   1   6   3   3   1   0   7   9   5   
3   6   4   4   2   0   8   9   6   4   9   4   5   2   1   3   0   9   10  7   
5   10  6   6   3   1   4   2   0   10  11  8   6   11  6   9   3   2   5   3   
2   0   11  11  10  8   11  7   9   4   3   6   4   5   0   12  11  10  9   13  

First element >=  1000:  1001 in position  24255

First element >=  2000:  2009 in position  43301

First element >=  3000:  3001 in position  61708

First element >=  4000:  4003 in position  81456

First element >=  5000:  5021 in position  98704

First element >=  6000:  6009 in position 121342

First element >=  7000:  7035 in position 151756

First element >=  8000:  8036 in position 168804

First element >=  9000:  9014 in position 184428

First element >= 10000: 10007 in position 201788

Raku

use Lingua::EN::Numbers;

my ($i, %i) = 0;

my @inventory = (^∞).map: {
    my $count = %i{$i} // 0;
    $i = $count ?? $i+1 !! 0;
    ++%i{$count};
    $count
}

say "Inventory sequence, first 100 elements:\n" ~
  @inventory[^100].batch(20)».fmt("%2d").join: "\n";

say '';

for (1..10).map: * × 1000 {
    my $k = @inventory.first: * >= $_, :k;
    printf "First element >= %6s is %6s in position: %s\n",
      .&comma, @inventory[$k].&comma, comma $k;
}


use SVG;
use SVG::Plot;

my @x = ^10000;

'Inventory-raku.svg'.IO.spurt:
 SVG.serialize: SVG::Plot.new(
    background  => 'white',
    width       => 1000,
    height      => 600,
    plot-width  => 950,
    plot-height => 550,
    x           => @x,
    values      => [@inventory[@x],],
    title       => "Inventory Sequence - First {+@x} values (zero indexed)",
).plot: :lines;
Output:
Inventory sequence, first 100 elements:
 0  1  1  0  2  2  2  0  3  2  4  1  1  0  4  4  4  1  4  0
 5  5  4  1  6  2  1  0  6  7  5  1  6  3  3  1  0  7  9  5
 3  6  4  4  2  0  8  9  6  4  9  4  5  2  1  3  0  9 10  7
 5 10  6  6  3  1  4  2  0 10 11  8  6 11  6  9  3  2  5  3
 2  0 11 11 10  8 11  7  9  4  3  6  4  5  0 12 11 10  9 13

First element >=  1,000 is  1,001 in position: 24,255
First element >=  2,000 is  2,009 in position: 43,301
First element >=  3,000 is  3,001 in position: 61,708
First element >=  4,000 is  4,003 in position: 81,456
First element >=  5,000 is  5,021 in position: 98,704
First element >=  6,000 is  6,009 in position: 121,342
First element >=  7,000 is  7,035 in position: 151,756
First element >=  8,000 is  8,036 in position: 168,804
First element >=  9,000 is  9,014 in position: 184,428
First element >= 10,000 is 10,007 in position: 201,788

converted to a .png to reduce size for display here: