Path: blob/master/sage/combinat/non_decreasing_parking_function.py
4045 views
r"""1Non-Decreasing Parking Functions23A *non-decreasing parking function* of size `n` is a non-decreasing4function `f` from `\{1,\dots,n\}` to itself such that for all `i`, one5has `f(i) \leq i`.67The number of non-decreasing parking functions of size `n` is the `n`-th8:func:`Catalan number<sage.combinat.combinat.catalan_number>`.910The set of non-decreasing parking functions of size `n` is in bijection with11the set of :mod:`Dyck words<sage.combinat.dyck_word>` of size `n`.1213AUTHORS:1415- Florent Hivert (2009-04)16"""17#*****************************************************************************18# Copyright (C) 2007 Florent Hivert <[email protected]>,19#20# Distributed under the terms of the GNU General Public License (GPL)21#22# This code is distributed in the hope that it will be useful,23# but WITHOUT ANY WARRANTY; without even the implied warranty of24# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU25# General Public License for more details.26#27# The full text of the GPL is available at:28#29# http://www.gnu.org/licenses/30#*****************************************************************************31from sage.rings.integer import Integer32from combinat import (CombinatorialClass, CombinatorialObject,33InfiniteAbstractCombinatorialClass, catalan_number)34from copy import copy353637def NonDecreasingParkingFunctions(n=None):38r"""39Returns the combinatorial class of Non-Decreasing Parking Functions.4041A *non-decreasing parking function* of size `n` is a non-decreasing42function `f` from `\{1,\dots,n\}` to itself such that for all `i`,43one has `f(i) \leq i`.4445EXAMPLES:4647Here are all the-non decreasing parking functions of size 5::4849sage: NonDecreasingParkingFunctions(3).list()50[[1, 1, 1], [1, 1, 2], [1, 1, 3], [1, 2, 2], [1, 2, 3]]5152If no size is specified, then NonDecreasingParkingFunctions53returns the combinatorial class of all non-decreasing parking functions.5455::5657sage: PF = NonDecreasingParkingFunctions(); PF58Non-decreasing parking functions59sage: [] in PF60True61sage: [1] in PF62True63sage: [2] in PF64False65sage: [1,1,3] in PF66True67sage: [1,1,4] in PF68False6970If the size `n` is specified, then NonDecreasingParkingFunctions returns71combinatorial class of all non-decreasing parking functions of size `n`.7273::7475sage: PF = NonDecreasingParkingFunctions(0)76sage: PF.list()77[[]]78sage: PF = NonDecreasingParkingFunctions(1)79sage: PF.list()80[[1]]81sage: PF = NonDecreasingParkingFunctions(3)82sage: PF.list()83[[1, 1, 1], [1, 1, 2], [1, 1, 3], [1, 2, 2], [1, 2, 3]]8485sage: PF3 = NonDecreasingParkingFunctions(3); PF386Non-decreasing parking functions of size 387sage: [] in PF388False89sage: [1] in PF390False91sage: [1,1,3] in PF392True93sage: [1,1,4] in PF394False9596TESTS::9798sage: PF = NonDecreasingParkingFunctions(5)99sage: len(PF.list()) == PF.cardinality()100True101102"""103if n is None:104return NonDecreasingParkingFunctions_all()105else:106assert isinstance(n, (Integer, int)) and n >= 0, '%s is not a non-negative integer.' % n107return NonDecreasingParkingFunctions_n(n)108109def is_a(x, n=None):110"""111Check whether a list is a non-decreasing parking function. If a size112`n` is specified, checks if a list is a non-decreasing parking113function of size `n`.114115TESTS::116117sage: from sage.combinat.non_decreasing_parking_function import is_a118sage: is_a([1,1,2])119True120sage: is_a([1,1,4])121False122sage: is_a([1,1,3], 3)123True124"""125if not isinstance(x, list):126return False127prev = 1128for i, elt in enumerate(x):129if prev > elt or elt > i+1:130return False131prev = elt132if n is not None and n != len(x):133return False134return True135136137class NonDecreasingParkingFunctions_all(InfiniteAbstractCombinatorialClass):138def __init__(self):139"""140TESTS::141142sage: DW = NonDecreasingParkingFunctions()143sage: DW == loads(dumps(DW))144True145"""146pass147148def __repr__(self):149"""150TESTS::151152sage: repr(NonDecreasingParkingFunctions())153'Non-decreasing parking functions'154"""155return "Non-decreasing parking functions"156157def __contains__(self, x):158"""159TESTS::160161sage: [] in NonDecreasingParkingFunctions()162True163sage: [1] in NonDecreasingParkingFunctions()164True165sage: [2] in NonDecreasingParkingFunctions()166False167sage: [1,1,3] in NonDecreasingParkingFunctions()168True169sage: [1,1,4] in NonDecreasingParkingFunctions()170False171"""172if isinstance(x, NonDecreasingParkingFunction):173return True174return is_a(x)175176def _infinite_cclass_slice(self, n):177"""178Needed by InfiniteAbstractCombinatorialClass to buid __iter__.179180TESTS::181182sage: (NonDecreasingParkingFunctions()._infinite_cclass_slice(4)183... == NonDecreasingParkingFunctions(4))184True185sage: it = iter(NonDecreasingParkingFunctions()) # indirect doctest186sage: [it.next() for i in range(8)]187[[], [1], [1, 1], [1, 2], [1, 1, 1], [1, 1, 2], [1, 1, 3], [1, 2, 2]]188"""189return NonDecreasingParkingFunctions_n(n)190191192class NonDecreasingParkingFunctions_n(CombinatorialClass):193"""194The combinatorial class of non-decreasing parking functions of195size `n`.196197A *non-decreasing parking function* of size `n` is a non-decreasing198function `f` from `\{1,\dots,n\}` to itself such that for all `i`,199one has `f(i) \leq i`.200201The number of non-decreasing parking functions of size `n` is the202`n`-th Catalan number.203204EXAMPLES::205206sage: PF = NonDecreasingParkingFunctions(3)207sage: PF.list()208[[1, 1, 1], [1, 1, 2], [1, 1, 3], [1, 2, 2], [1, 2, 3]]209sage: PF = NonDecreasingParkingFunctions(4)210sage: PF.list()211[[1, 1, 1, 1], [1, 1, 1, 2], [1, 1, 1, 3], [1, 1, 1, 4], [1, 1, 2, 2], [1, 1, 2, 3], [1, 1, 2, 4], [1, 1, 3, 3], [1, 1, 3, 4], [1, 2, 2, 2], [1, 2, 2, 3], [1, 2, 2, 4], [1, 2, 3, 3], [1, 2, 3, 4]]212sage: [ NonDecreasingParkingFunctions(i).cardinality() for i in range(10)]213[1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862]214215.. warning::216217The precise order in which the parking function are generated or218listed is not fixed, and may change in the future.219220AUTHORS:221222- Florent Hivert223"""224def __init__(self, n):225"""226TESTS::227228sage: PF = NonDecreasingParkingFunctions(3)229sage: PF == loads(dumps(PF))230True231"""232self.n = n233234def __repr__(self):235"""236TESTS::237238sage: repr(NonDecreasingParkingFunctions(3))239'Non-decreasing parking functions of size 3'240"""241return "Non-decreasing parking functions of size %s"%(self.n)242243def __contains__(self, x):244"""245TESTS::246247sage: PF3 = NonDecreasingParkingFunctions(3); PF3248Non-decreasing parking functions of size 3249sage: [] in PF3250False251sage: [1] in PF3252False253sage: [1,1,3] in PF3254True255sage: [1,1,1] in PF3256True257sage: [1,1,4] in PF3258False259sage: all([p in PF3 for p in PF3])260True261"""262if isinstance(x, NonDecreasingParkingFunction):263return True264return is_a(x, self.n)265266def cardinality(self):267"""268Returns the number of non-decreasing parking functions of size269`n`. This number is the `n`-th :func:`Catalan270number<sage.combinat.combinat.catalan_number>`.271272EXAMPLES::273274sage: PF = NonDecreasingParkingFunctions(0)275sage: PF.cardinality()2761277sage: PF = NonDecreasingParkingFunctions(1)278sage: PF.cardinality()2791280sage: PF = NonDecreasingParkingFunctions(3)281sage: PF.cardinality()2825283sage: PF = NonDecreasingParkingFunctions(5)284sage: PF.cardinality()28542286"""287return catalan_number(self.n)288289def __iter__(self):290"""291Returns an iterator for non-decreasing parking functions of size `n`.292293.. warning::294295The precise order in which the parking function are296generated is not fixed, and may change in the future.297298EXAMPLES::299300sage: PF = NonDecreasingParkingFunctions(0)301sage: [e for e in PF] # indirect doctest302[[]]303sage: PF = NonDecreasingParkingFunctions(1)304sage: [e for e in PF] # indirect doctest305[[1]]306sage: PF = NonDecreasingParkingFunctions(3)307sage: [e for e in PF] # indirect doctest308[[1, 1, 1], [1, 1, 2], [1, 1, 3], [1, 2, 2], [1, 2, 3]]309sage: PF = NonDecreasingParkingFunctions(4)310sage: [e for e in PF] # indirect doctest311[[1, 1, 1, 1], [1, 1, 1, 2], [1, 1, 1, 3], [1, 1, 1, 4], [1, 1, 2, 2], [1, 1, 2, 3], [1, 1, 2, 4], [1, 1, 3, 3], [1, 1, 3, 4], [1, 2, 2, 2], [1, 2, 2, 3], [1, 2, 2, 4], [1, 2, 3, 3], [1, 2, 3, 4]]312313TESTS::314315sage: PF = NonDecreasingParkingFunctions(5)316sage: [e for e in PF] == PF.list()317True318sage: PF = NonDecreasingParkingFunctions(6)319sage: [e for e in PF] == PF.list()320True321322Complexity: constant amortized time.323"""324# FIXME : currently composition is extremenly slow.325# Activate the following code as soon as compositions use326# the integer_list_lex machinery327# for i in range(self.n, self.n*(self.n+1)/2+1):328# for z in Compositions(i, length=self.n, outer=range(1, self.n+1),329# min_slope=0).__iter__():330# yield NonDecreasingParkingFunction(z._list)331# return332def iterator_rec(n):333"""334TESTS::335336sage: PF = NonDecreasingParkingFunctions(2)337sage: [e for e in PF] # indirect doctest338[[1, 1], [1, 2]]339"""340if n==0:341yield [ ]; return342if n==1:343yield [1]; return344for res1 in iterator_rec(n-1):345for i in range(res1[-1], n+1):346res = copy(res1)347res.append(i)348yield res349return350for res in iterator_rec(self.n):351yield NonDecreasingParkingFunction(res)352return353354class NonDecreasingParkingFunction(CombinatorialObject):355"""356A *non decreasing parking function* of size `n` is a non-decreasing357function `f` from `\{1,\dots,n\}` to itself such that for all `i`,358one has `f(i) \leq i`.359360EXAMPLES::361362sage: NonDecreasingParkingFunction([])363[]364sage: NonDecreasingParkingFunction([1])365[1]366sage: NonDecreasingParkingFunction([2])367Traceback (most recent call last):368...369AssertionError: [2] is not a non-decreasing parking function.370sage: NonDecreasingParkingFunction([1,2])371[1, 2]372sage: NonDecreasingParkingFunction([1,1,2])373[1, 1, 2]374sage: NonDecreasingParkingFunction([1,1,4])375Traceback (most recent call last):376...377AssertionError: [1, 1, 4] is not a non-decreasing parking function.378"""379def __init__(self, lst):380"""381TESTS::382383sage: NonDecreasingParkingFunction([1, 1, 2, 2, 5, 6])384[1, 1, 2, 2, 5, 6]385"""386assert is_a(lst), '%s is not a non-decreasing parking function.' % lst387CombinatorialObject.__init__(self, lst)388389def __getitem__(self, n):390"""391Returns the `n^{th}` item in the underlying list.392393.. note::394395Note that this is different than the image of ``n`` under396function. It is "off by one".397398EXAMPLES::399400sage: p = NonDecreasingParkingFunction([1, 1, 2, 2, 5, 6])401sage: p[0]4021403sage: p[2]4042405"""406return self._list[n]407408def __call__(self, n):409"""410Returns the image of ``n`` under the parking function.411412EXAMPLES::413414sage: p = NonDecreasingParkingFunction([1, 1, 2, 2, 5, 6])415sage: p(3)4162417sage: p(6)4186419"""420return self._list[n-1]421422def __mul__(self, lp):423"""424The composition of non-decreasing parking functions.425426EXAMPLES::427428sage: p = NonDecreasingParkingFunction([1,1,3])429sage: q = NonDecreasingParkingFunction([1,2,2])430sage: p * q431[1, 1, 1]432sage: q * p433[1, 1, 2]434"""435lp = lp._list436sp = self._list437lp = lp[:] + [i+1 for i in range(len(lp), len(lp))]438sp = sp[:] + [i+1 for i in range(len(sp), len(lp))]439return NonDecreasingParkingFunction([ sp[i-1] for i in lp ])440441def to_dyck_word(self):442"""443Implements the bijection to :class:`Dyck444words<sage.combinat.dyck_word.DyckWords>`, which is defined as follows.445Take a non decreasing parking function, say [1,1,2,4,5,5], and draw446its graph::447448.449. |450.__.__|451.__| . .452. | . . .453. .__| . . .454.__.__| . . . .4551 1 2 4 5 5456457The corresponding Dyck word [1,1,0,1,0,0,1,0,1,1,0,0] is then read off458from the sequence of horizontal and vertical steps. The converse459bijection is :meth:`.from_dyck_word`.460461EXAMPLES::462463sage: NonDecreasingParkingFunction([1,1,2,4,5,5]).to_dyck_word()464[1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0]465sage: NonDecreasingParkingFunction([]).to_dyck_word()466[]467sage: NonDecreasingParkingFunction([1,1,1]).to_dyck_word()468[1, 1, 1, 0, 0, 0]469sage: NonDecreasingParkingFunction([1,2,3]).to_dyck_word()470[1, 0, 1, 0, 1, 0]471sage: NonDecreasingParkingFunction([1,1,3,3,4,6,6]).to_dyck_word()472[1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0]473474TESTS::475476sage: ndpf=NonDecreasingParkingFunctions(5);477sage: list(ndpf) == [pf.to_dyck_word().to_non_decreasing_parking_function() for pf in ndpf]478True479"""480from sage.combinat.dyck_word import DyckWord_class481return DyckWord_class.from_non_decreasing_parking_function(self)482483@classmethod484def from_dyck_word(cls, dw):485"""486Bijection from :class:`Dyck487words<sage.combinat.dyck_word.DyckWords>`. It is the inverse of the488bijection :meth:`.to_dyck_word`. You can find there the mathematical489definition.490491EXAMPLES::492493sage: NonDecreasingParkingFunction.from_dyck_word([])494[]495sage: NonDecreasingParkingFunction.from_dyck_word([1,0])496[1]497sage: NonDecreasingParkingFunction.from_dyck_word([1,1,0,0])498[1, 1]499sage: NonDecreasingParkingFunction.from_dyck_word([1,0,1,0])500[1, 2]501sage: NonDecreasingParkingFunction.from_dyck_word([1,0,1,1,0,1,0,0,1,0])502[1, 2, 2, 3, 5]503504TESTS::505506sage: ndpf=NonDecreasingParkingFunctions(5);507sage: list(ndpf) == [NonDecreasingParkingFunction.from_dyck_word(pf.to_dyck_word()) for pf in ndpf]508True509"""510res = []511val = 1512for i in dw:513if i == 0:514val+=1515else:516res.append(val)517return cls(res)518519520521