omz:forum

    • Register
    • Login
    • Search
    • Recent
    • Popular

    Welcome!

    This is the community forum for my apps Pythonista and Editorial.

    For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.


    Function for recognize quantity of unique combinations

    Pythonista
    6
    37
    10361
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • cvp
      cvp @cvp last edited by cvp

      @lyubomyr83 sorry if I didn't understand correctly, do you want a formula or a script like

      def uniques_col(operators, max):
      	n = 0
      	if operators == '+':
      		for i in range(1,max):
      			for j in range(i,max-i+1):
      				n += 1
      				print(i,operators,j,'=',i+j)
      	elif operators == '-':
      		for i in range(1,max+1):
      			for j in range(1,i+1):
      				n += 1
      				print(i,operators,j,'=',i-j)
      	return n
      
      op = input('operator')
      v = int(input(op))
      print(uniques_col(op,v))
      
      lyubomyr83 1 Reply Last reply Reply Quote 1
      • bennr01
        bennr01 @lyubomyr83 last edited by bennr01

        @lyubomyr83 Something like this?

        # -*- coding: utf-8 -*-
        """
        Test solution for https://forum.omz-software.com/topic/6159/function-for-recognize-quantity-of-unique-combinations
        """
        import operator
        
        
        OPERATORS = {
            "+": operator.add,
            "-": operator.sub,
            "*": operator.mul,
            "×": operator.mul,
        }
        
        
        def eval_eq(a, op, b):
            return OPERATORS[op](a, b)
        
        
        def uniques_col(operators, max):
            if len(operators) == 0:
                # we could also just return (max - 1)
                raise ValueError("No operators given!")
            elif len(operators) > 1:
                # the code in the question makes it look like we should add
                # the number of individual operator combinations if multiple
                # operators are given.
                n = 0
                for op in operators:
                    n += uniques_col(op, max)
                return n
            else:
                combinations = []
                for a in range(max, 0, -1):
                # for a in range(1, max + 1):
                    for b in range(max, 0, -1):
                    # for b in range(1, max + 1):
                        res = eval_eq(a, operators, b)
                        if res > max:
                            # should this be result >= max?
                            continue
                        if res < 0:
                            # result cant be under 0
                            continue
                        comb = (a, operators, b)
                        print(comb)
                        if comb not in combinations:
                            combinations.append(comb)
                return len(combinations)
        
        
        if __name__ == "__main__":
            testdata = [
                # format: operators, max, expected
                ("-", 2, 3),
                ("+", 2, 1),
                ("+-", 2, 4),
                ("+-", 3, 9),
                ("+-*", 3, None),
            ]
            for ops, m, exp in testdata:
                n = uniques_col(ops, m)
                print("uniques_col({m}, '{o}') -> {r}; expected {e}".format(m=m, o=ops, r=n, e=exp))
        

        This produced this output:

        (2, '-', 2)
        (2, '-', 1)
        (1, '-', 1)
        uniques_col(2, '-') -> 3; expected 3
        (1, '+', 1)
        uniques_col(2, '+') -> 1; expected 1
        (1, '+', 1)
        (2, '-', 2)
        (2, '-', 1)
        (1, '-', 1)
        uniques_col(2, '+-') -> 4; expected 4
        (2, '+', 1)
        (1, '+', 2)
        (1, '+', 1)
        (3, '-', 3)
        (3, '-', 2)
        (3, '-', 1)
        (2, '-', 2)
        (2, '-', 1)
        (1, '-', 1)
        uniques_col(3, '+-') -> 9; expected 9
        (2, '+', 1)
        (1, '+', 2)
        (1, '+', 1)
        (3, '-', 3)
        (3, '-', 2)
        (3, '-', 1)
        (2, '-', 2)
        (2, '-', 1)
        (1, '-', 1)
        (3, '*', 1)
        (2, '*', 1)
        (1, '*', 3)
        (1, '*', 2)
        (1, '*', 1)
        uniques_col(3, '+-*') -> 14; expected None
        
        

        Your question is kind of hard to understand, so I am not sure this solution is correct...

        mikael 1 Reply Last reply Reply Quote 1
        • mikael
          mikael @bennr01 last edited by mikael

          @bennr01, nice! Couple of possible, nit-picky refinements:

          Do not use max as an argument name.

          Instead of for loops, generate the candidates with:

          candidates = itertools.product(
              range(max_int+1), operations, range(max_int+1)
          )
          
          bennr01 1 Reply Last reply Reply Quote 1
          • bennr01
            bennr01 @mikael last edited by

            @mikael said:

            Do not use max as an argument name.

            Generally true, but in this case I wanted to preserve the original function signature used in the question.

            Instead of for loops, generate the candidates with:

            candidates = itertools.product(
                range(max_int+1), operations, range(max_int+1)
            )
            

            Yeah, that is way cleaner.

            mikael 2 Replies Last reply Reply Quote 0
            • mikael
              mikael @bennr01 last edited by

              @bennr01, just read an article on functional programming in Python, so one step further:

              import itertools
              import operator
              
              OPERATORS = {
                  "+": operator.add,
                  "-": operator.sub,
                  "*": operator.mul,
                  "×": operator.mul,
              }
              
              def uniques_col(operators, max_value):
                  return filter(
                      lambda c: 0 <= OPERATORS[c[1]](c[0], c[2]) <= max_value,
                      itertools.product(
                          range(1, max_value+1),
                          operators,
                          range(1, max_value+1)
                      )
                  )
              
              print(list(uniques_col('+-', 2)))
              
              1 Reply Last reply Reply Quote 1
              • mikael
                mikael @lyubomyr83 last edited by

                @lyubomyr83, not an answer to your question, but maybe you can use these brute-force versions to check the formulas you come up with?

                1 Reply Last reply Reply Quote 0
                • mikael
                  mikael @bennr01 last edited by mikael

                  @bennr01, with one more fancy function:

                  import itertools
                  
                  def uniques_col(operators, max_value):
                      possibles = lambda: range(1, max_value+1)
                      return filter(
                          lambda c: 0 <= eval(''.join(map(str, c))) <= max_value,
                          itertools.product(possibles(), operators, possibles())
                      )
                  
                  print(list(uniques_col('+-', 2)))
                  
                  1 Reply Last reply Reply Quote 1
                  • lyubomyr83
                    lyubomyr83 @cvp last edited by lyubomyr83

                    @cvp, thank you all for help!!!
                    With your function i receive right col for '-', but not for '+':

                    def uniques_col(operators, max):
                    n = 0
                    if operators == '+':
                    for i in range(1,max):
                    for j in range(i,max-i+1):
                    n += 1
                    print(i,operators,j,'=',i+j)
                    elif operators == '-':
                    for i in range(1,max+1):
                    for j in range(1,i+1):
                    n += 1
                    print(i,operators,j,'=',i-j)
                    return n

                    while True:
                    op = input('operator\n')
                    max = int(input('max\n'))
                    print(uniques_col(op,max))

                    What i receive:
                    operator-
                    max4
                    1 - 1 = 0
                    2 - 1 = 1
                    2 - 2 = 0
                    3 - 1 = 2
                    3 - 2 = 1
                    3 - 3 = 0
                    4 - 1 = 3
                    4 - 2 = 2
                    4 - 3 = 1
                    4 - 4 = 0
                    10
                    operator+
                    max4
                    1 + 1 = 2
                    1 + 2 = 3
                    1 + 3 = 4
                    2 + 2 = 4
                    4

                    Where is 1+3 and 2+1, so for '+' i need receive 6 unique examples.

                    cvp 2 Replies Last reply Reply Quote 0
                    • cvp
                      cvp @lyubomyr83 last edited by cvp

                      @lyubomyr83 said:

                      Where is 1+3 and 2+1,

                      Ok, I did believe that 1+2 is the same as 2+1, thus I did not generate it.
                      As I told you, I was not sure to correctly understand 😀

                      Thus it is better to use the other script (of @bennr01 and @mikael ) because it tries all combinations.

                      1 Reply Last reply Reply Quote 0
                      • cvp
                        cvp @lyubomyr83 last edited by cvp

                        @lyubomyr83

                        Or Change into

                            if operators == '+':
                                for i in range(1,max):
                                    for j in range(1,max-i+1): 
                        
                        lyubomyr83 1 Reply Last reply Reply Quote 0
                        • JonB
                          JonB last edited by

                          i think you would want to check 1 to max for both numbers (consider /, max/max=1)

                          cvp 1 Reply Last reply Reply Quote 0
                          • cvp
                            cvp @JonB last edited by cvp

                            @JonB if you do that, you have to check and skip cases where i+j>max
                            With « my » formula, no check is needed

                            1 Reply Last reply Reply Quote 0
                            • ccc
                              ccc last edited by

                              Combinatoric iterators:

                              • https://docs.python.org/3/library/itertools.html#itertools.combinations
                              • https://docs.python.org/3/library/itertools.html#itertools.combinations_with_replacement
                              • https://docs.python.org/3/library/itertools.html#itertools.permutations
                              cvp 1 Reply Last reply Reply Quote 0
                              • cvp
                                cvp @ccc last edited by

                                @ccc I'm always positively surprised by the number of libraries in Python

                                1 Reply Last reply Reply Quote 0
                                • mikael
                                  mikael last edited by

                                  I went into some kind of half-insane readability/conciseness optimization/noodling loop on this. Here’s the latest version:

                                  from itertools import product
                                  
                                  def uniques_col(ops, maximum):
                                      number_range = range(1, maximum+1)
                                      numbers = list(map(str, number_range))
                                      return filter(
                                          lambda c: 0 <= eval(''.join(c)) <= maximum,
                                          product(numbers, ops, numbers)
                                      )
                                  
                                  uniques = uniques_col('+-/', 2)
                                  
                                  print(*[
                                      f"{i+1:4}: {''.join(c)}"
                                      for i, c in enumerate(uniques)],
                                      sep='\n'
                                  )
                                  
                                  1 Reply Last reply Reply Quote 1
                                  • ccc
                                    ccc last edited by ccc

                                    numbers = [str(i + 1) for i in range(maximum)]

                                    Read “What’s new in Python 3” for a discussion on avoiding map(), reduce(), filter().

                                    mikael 2 Replies Last reply Reply Quote 1
                                    • mikael
                                      mikael @ccc last edited by

                                      @ccc, you take the prize for conciseness, and readability is not bad either.

                                      I was debating the value of separating the range of numbers (problem domain issue) and the conversion to strings (a technical implementation detail).

                                      cvp 1 Reply Last reply Reply Quote 0
                                      • cvp
                                        cvp @mikael last edited by

                                        @mikael and @ccc I think that I'll stop to believe I can program in Python 😢

                                        mikael 1 Reply Last reply Reply Quote 0
                                        • mikael
                                          mikael @ccc last edited by

                                          @ccc said:

                                          What’s new in Python 3

                                          For clarity, What’s new in Python 3 does not advice against using map and filter as such, but against using list(map(...)) when a list comprehension can be used instead.

                                          Thus, as already said, your amendment makes a lot of sense, but it does not automatically follow that we would change the filter into a comprehension, if we want to leave it up to the user of the function to decide whether to ”collapse” the iterator or not.

                                          1 Reply Last reply Reply Quote 0
                                          • mikael
                                            mikael @cvp last edited by

                                            @cvp, ha ha. There’s a huge difference and swiftly diminishing returns between ”getting to results” and ”getting to perfect”. While this kind of noodling is fun (for me), your recent track record of real results speaks for itself.

                                            cvp 1 Reply Last reply Reply Quote 0
                                            • First post
                                              Last post
                                            Powered by NodeBB Forums | Contributors