Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Help with coding/ideas
#1
Can you help me with that please? I need to write a function max(L) which, for a given int-list L of length n≥1, returns the maximum length of an unimodular sequence.
(a sequence is called unimodular in case a0≤a1≤...≤ai≥ai+1≥...ad )
For example :
L=[1,2,3,2,1,3,6,3,6]
L contains two unimodular sequences : 1,2,3,2,1 of length 5 and 1,3,6,3 of length 4.
The first one has maximum length, therefore max(L) should return the value 5.
If you have any ideas from where i can start, share it with me!
thank you.
Reply
#2
This problem has a fairly obvious solution and probably a more subtle solution I haven't come up with yet. I don't know if there can be anything more optimized than the obvious solution because it finds the longest unimodular sequence in a sequence of size N in N steps.

Think of the list of numbers as a mountain range, each number representing altitude. You start at 1 and climb to 2, then further up you go from 2 to 3. The next step after 3 is 2, so you switch from ascending to descending. You descend from 3 to 2, then from 2 to 1. Your next step will have you start ascending again, so your descent is complete. You traversed the mountain. How many steps did it take?

If you have multiple mountains the process repeats. If you want to know the "longest" mountain you keep a record. The first mountain is the longest until you traverse a longer mountain, then it becomes the longest.

There are three tricky parts. You have to start with an ascent. If the sequence starts with numbers descending, how do you handle that? What do you do when you reach the end of the mountain range? Do you have a mountain without a descent? The final tricky part is what to do about valleys. If you sequence is [1,2,3,2,1,1,3,6,3,6], the two unimodular sequences are [1,2,3,2,1,1] and [1,1,3,6,3]. How do you count the flat valley as being part of two mountains?
Gribouillis and natalie like this post
Reply
#3
Since you have to increase then decrease, you need to go through the list checking each number against the next number, until the next number is lower. You have your high number.

Then you need carry on from 1 after your high number, looking for the next number that is higher than the previous number. Then you have lowest number and your sequence.

If the length of your sequence is longer than the length of the list which remains to be checked, then you know you have the longest sequence. You could add that check.

The minimum size of a sequence is 3, like: [1,2,1] so 3 plays a role here.

I was supposed to be doing something else, but I solved my PHP problem this morning, so I took a look at this. Interesting.

Finding the sequences is the problem. Finding the longest one is trivial after that.

This may give you some ideas, although, I haven't tried it on a randomly generated list. You may need to tweak!!

You could make a randomly generated list like this:

import random
mylist = []
length = 54 # or any number
for i in range(1, length):
    num = random.randint(1, length)
    mylist.append(num)
mylist = [1,1,2,1,3,5,7,9,7,11,12,14,16,15,14,24,34,44,55,54,61,62,63,64,65,66,1,2,3]

def myApp(mylist):
    # find the maximums
    def getTempmax(go_up, alist):
        for i in range(go_up, len(alist)-1):
            num1 = alist[i]
            num2 = alist[i+1]
            print(num1, num2)
            if num2 < num1:
                print('temporary maximum is', num1)
                tup = (i+1, num1)
                print('tup_max is', tup)
                return tup

    # find the minimums
    def getTempmin(go_down, alist):
        if len(alist) - go_down < 3:
            go_down = len(alist)
            print('temporary minimum is', alist[-1])            
            return (go_down, alist[-1])
        for j in range(go_down, len(alist)-1):
            num3 = alist[j]
            num4 = alist[j+1]
            if num4 > num3:
                print('temporary minimum is', num3)
                tup = (j, num3)
                print('tup_min is', tup)
                return tup

    # gotta start somewhere
    def initialize(passlist):
        print('This list is', len(passlist), 'long')
        tup_max = getTempmax(0, passlist)
        tup_min = getTempmin(tup_max[0], passlist)    
        alist = []
        sequence = passlist[0:tup_min[0]+1]
        alist.append(sequence)
        print('First sequence is', sequence)
        tup = (tup_max, tup_min, alist)
        return tup

    #mylist = [1,2,3,2,1,3,6,3,6,7,8,4,3,9,1,1,2,3,2,2,1,4,5,4,9,7,3,10,11]
    start = initialize(mylist)
    tup_max = start[0]
    tup_min = start[1]
    sequences = start[2]

    while not len(mylist) - tup_min[0] <= 3:
        count = tup_min[0]
        tup_max = getTempmax(tup_min[0], mylist)
        tup_min = getTempmin(tup_max[0], mylist)
        sequence = mylist[count:tup_min[0]+1]
        # the last sequence need special treatment
        if sequence[-1] > sequence[-2]:
            del sequence[-1]
        print('sequence is', sequence)
        sequences.append(sequence)
        
    # now just loop through sequences to find the longest list
    biggest_list = 0
    for s in sequences:
        longest_list = len(s)
        if longest_list > biggest_list:
            biggest_list = longest_list
            print('最长的列出为:', biggest_list)
Reply
#4
Following deanhystad's reply, the below function can help. It transforms the list into a string where 'a' means ascent, 'd' means descent and 'f' means flat. The result can be computed from the string.
>>> def _idiff(L):
...     for a, b in zip(L, L[1:]):
...         yield 'a' if a < b else ('f' if a == b else 'd')
... 
>>> def idiff(L):
...     return ''.join(_idiff(L))
... 
>>> L = [1,2,3,2,1,1,3,6,3,6]
>>> idiff(L)
'aaddfaada'
>>> 
natalie likes this post
Reply
#5
(Feb-10-2022, 07:29 AM)Pedroski55 Wrote: Since you have to increase then decrease, you need to go through the list checking each number against the next number, until the next number is lower. You have your high number.

Then you need carry on from 1 after your high number, looking for the next number that is higher than the previous number. Then you have lowest number and your sequence.

If the length of your sequence is longer than the length of the list which remains to be checked, then you know you have the longest sequence. You could add that check.

The minimum size of a sequence is 3, like: [1,2,1] so 3 plays a role here.

I was supposed to be doing something else, but I solved my PHP problem this morning, so I took a look at this. Interesting.

Finding the sequences is the problem. Finding the longest one is trivial after that.

This may give you some ideas, although, I haven't tried it on a randomly generated list. You may need to tweak!!

You could make a randomly generated list like this:

import random
mylist = []
length = 54 # or any number
for i in range(1, length):
    num = random.randint(1, length)
    mylist.append(num)
mylist = [1,1,2,1,3,5,7,9,7,11,12,14,16,15,14,24,34,44,55,54,61,62,63,64,65,66,1,2,3]

def myApp(mylist):
    # find the maximums
    def getTempmax(go_up, alist):
        for i in range(go_up, len(alist)-1):
            num1 = alist[i]
            num2 = alist[i+1]
            print(num1, num2)
            if num2 < num1:
                print('temporary maximum is', num1)
                tup = (i+1, num1)
                print('tup_max is', tup)
                return tup

    # find the minimums
    def getTempmin(go_down, alist):
        if len(alist) - go_down < 3:
            go_down = len(alist)
            print('temporary minimum is', alist[-1])            
            return (go_down, alist[-1])
        for j in range(go_down, len(alist)-1):
            num3 = alist[j]
            num4 = alist[j+1]
            if num4 > num3:
                print('temporary minimum is', num3)
                tup = (j, num3)
                print('tup_min is', tup)
                return tup

    # gotta start somewhere
    def initialize(passlist):
        print('This list is', len(passlist), 'long')
        tup_max = getTempmax(0, passlist)
        tup_min = getTempmin(tup_max[0], passlist)    
        alist = []
        sequence = passlist[0:tup_min[0]+1]
        alist.append(sequence)
        print('First sequence is', sequence)
        tup = (tup_max, tup_min, alist)
        return tup

    #mylist = [1,2,3,2,1,3,6,3,6,7,8,4,3,9,1,1,2,3,2,2,1,4,5,4,9,7,3,10,11]
    start = initialize(mylist)
    tup_max = start[0]
    tup_min = start[1]
    sequences = start[2]

    while not len(mylist) - tup_min[0] <= 3:
        count = tup_min[0]
        tup_max = getTempmax(tup_min[0], mylist)
        tup_min = getTempmin(tup_max[0], mylist)
        sequence = mylist[count:tup_min[0]+1]
        # the last sequence need special treatment
        if sequence[-1] > sequence[-2]:
            del sequence[-1]
        print('sequence is', sequence)
        sequences.append(sequence)
        
    # now just loop through sequences to find the longest list
    biggest_list = 0
    for s in sequences:
        longest_list = len(s)
        if longest_list > biggest_list:
            biggest_list = longest_list
            print('最长的列出为:', biggest_list)

Thank you so much for your time and help!
I don't know if I understood it correctly, if the list L=[1,2,3,4,1,1,1] should normally return 7?
When I test it with your code, an error appears.
Reply
#6
I'm happy if you got some ideas! Nice name, Natalie!

Maybe it's the Chinese! Change the Chinese for "The longest list is: "

I changed the last part to print each sequence:

# now just loop through sequences to find the longest list
    biggest_list = 0
    for s in sequences:
        print('Sequence is', s)
        longest_list = len(s)
        if longest_list > biggest_list:
            biggest_list = longest_list
            print('最长的列出为:', biggest_list)
One problem: I started with this list:

mylist = [12,1,2,3,1,2,3,4,5,6,7,6,1,2]

Then you get [12,1] as the first sequence. You can think of a way to avoid that!

I get this output in Idle:

Quote:Sequence is [12, 1]
最长的列出为: 2
Sequence is [1, 2, 3, 1]
最长的列出为: 4
Sequence is [1, 2, 3, 4, 5, 6, 7, 6, 1]
最长的列出为: 9

Make a random list for testing:

# generate a random list for testing
start = input('Enter a start number ... ')
stop = input('Enter a stop number ... ')
length = input('How long do you want your list? ')             
mylist = [random.randint(int(start), int(stop)) for i in range(int(length) + 1)]
Reply
#7
Since we are doing homework now.
def unimod(seq):
    """
    Return longest unimodal sequence in seq.
    A sequence is unimodular when a0≤a1≤...≤ai≥ai+1≥...ad
    """
    if len(seq) < 3:  # No sequences < 3 are unimodular(?)
        return []

    ascending = None  # We don't know if we are ascending or descending
    _, start, end = longest = (0, 0, 0)
    for p, n in zip(seq, seq[1:]):
        if ascending is None:
            # Have we found the first valley?
            if n >= p:
                ascending = True
                start = end
        elif ascending:
            # Are we still ascending?
            ascending = n >= p
        elif n > p:
            # Found start of next mountain.  Was last mountain the longest?
            ascending = True
            longest = max(longest, (end-start+1, start, end+1))
            # Back up to the start of the valley
            start = end
            while start > 0 and seq[start-1] == p:
                start -= 1
        end += 1
    if ascending == False:
        # Last sequence is unimodular.  Is it longest?
        longest = max(longest, (end-start+1, start, end+1))
    _, start, end = longest
    return seq[start:end]

# Test some sequences
assert(unimod([]) == [])
assert(unimod([1,2]) == [])
assert(unimod([2,1,2]) == [])
assert(unimod([5,4,3,2,1,2,3]) == [])
assert(unimod([1,2,1]) == [1,2,1])
assert(unimod([2,1,2,1,2]) == [1,2,1])
assert(unimod([5,4,3,2,1,2,1,2,3,4,5]) == [1,2,1])
assert(unimod([2,1,2,1,1,1,2,2,1]) == [1,1,1,2,2,1])
assert(unimod([1,2,3,2,1,2,3,3,2,1]) == [1,2,3,3,2,1])
This is a one trick pony. I prefer writing more generic tools and using them to solve multiple problems. With a small modification unimod becomes a unimodal sequence generator. I can still find the longest sequence using max(). And if I only want the length, len(max())
def unimod(seq):
    """
    Generate unimodal sequences found in seq.
    A sequence is unimodular when a0≤a1≤...≤ai≥ai+1≥...ad
    """
    if len(seq) < 3:  # No sequences < 3 are unimodular(?)
        return []

    ascending = None
    start = end = 0
    for p, n in zip(seq, seq[1:]):
        if ascending is None:
            # Have we found the first valley?
            if n >= p:
                ascending = True
                start = end
        elif ascending:
            # Are we still ascending?
            ascending = n >= p
        elif n > p:
            # Found start of next mountain.  Was last mountain the longest?
            ascending = True
            yield(seq[start:end+1])
            # Back up to the start of the valley
            start = end
            while start > 0 and seq[start-1] == p:
                start -= 1
        end += 1
    if ascending == False:
        # Last sequence is unimodular.  Is it longest?
        yield(seq[start, end+1])

sequences = list(unimod([3,2,1,2,1,2,3,2,1,2,3,4,3,2,1,2,1,2,3]))
longest = max(sequences, key=len)
print("Unimodal sequences =", sequences)
print("Longest sequence =", longest, 'Length =', len(longest))
Output:
Unimodal sequences = [[1, 2, 1], [1, 2, 3, 2, 1], [1, 2, 3, 4, 3, 2, 1], [1, 2, 1]] Longest sequence = [1, 2, 3, 4, 3, 2, 1] Length = 7
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Python Pandas Syntax problem? Wrong Output, any ideas? Gbuoy 2 937 Jan-18-2023, 10:02 PM
Last Post: snippsat
  Looking for some ideas - Radius Log moralear27 1 1,961 Sep-10-2020, 09:32 PM
Last Post: micseydel
  Dealing with a .json nightmare... ideas? t4keheart 10 4,419 Jan-28-2020, 10:12 PM
Last Post: t4keheart
  some ideas for intelligent list splitting? wardancer84 4 3,225 Nov-20-2018, 02:47 PM
Last Post: DeaD_EyE
  Ideas?? gullidog 3 3,740 Jul-20-2017, 09:06 PM
Last Post: sparkz_alot
  Ideas for creating a function Ganjena 0 2,714 May-27-2017, 08:57 PM
Last Post: Ganjena

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020