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 @lyubomyr83 last edited by

      @lyubomyr83 said:

      Minimum example result must be under 0

      upper 0

      cvp 1 Reply Last reply Reply Quote 1
      • 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
                                            • First post
                                              Last post
                                            Powered by NodeBB Forums | Contributors