There is no need for the initial product == 0
And you can simplify the elif
tree with early returns
def solve2(a, b, c):
if a == b == c:
return 1
if a == b:
return c
if a == c:
return b
if b == c:
return a
return a * b * c
This makes the intent and logic very clear.
You can use the fact that in python, True
and False
are used as 1 and 0 in calculations:
def my_product(a, b, c):
return (
a ** (a not in {b, c})
* b ** (b not in {a, c})
* c ** (c not in {a, b})
)
or
def my_product2(a, b, c):
return (
a ** (a != b and a != c)
* b ** (b != a and b != c)
* c ** (a != c and b != c)
)
or using the new python 3.8 math.prod
import math
def my_product_math(a, b, c):
return math.prod(
(
a if a not in {b, c} else 1,
b if b not in {a, c} else 1,
c if c not in {a, b} else 1,
)
)
Then you need a few test cases:
test_cases = {
(2, 3, 5): 30,
(3, 5, 3): 5,
(5, 3, 3): 5,
(3, 3, 3): 1,
(3, 3, 2): 2,
}
and you evaluate them like this:
[my_product(a,b,c) == result for (a,b,c), result in test_cases.items()]
You can even time this:
import timeit
timeit.timeit(
"[func(a,b,c) == result for (a,b,c), result in test_cases.items()]",
globals={"func": my_product, "test_cases": test_cases},
)
and the all together behind a main guard:
if __name__ == "__main__":
test_cases = {
(2, 3, 5): 30,
(3, 5, 3): 5,
(5, 3, 3): 5,
(3, 3, 3): 1,
(3, 3, 2): 2,
}
methods = [
solve,
solve2,
my_product,
my_product_math,
solitary_product,
solitary_numbers_product,
solve_graipher,
solve_kuiken,
solve_kuiken_without_lambda,
my_product2,
]
for method in methods:
result = all(
[
method(a, b, c) == result
for (a, b, c), result in test_cases.items()
]
)
time = timeit.timeit(
"[func(a,b,c) == result for (a,b,c), result in test_cases.items()]",
globals={"func": method, "test_cases": test_cases},
)
print(f"{method.__name__}: {result} - {time}")
Which shows that in terms of speed, your method is one of the fastest
solve: True - 2.324101332999817
solve2: True - 2.386756923000121
my_product: True - 6.072235077000187
my_product_math: True - 5.299641845999986
solitary_product: True - 19.69770133299994
solitary_numbers_product: True - 2.4141538469998522
solve_graipher: True - 4.152514871999756
solve_kuiken: True - 7.715469948999726
solve_kuiken_without_lambda: True - 5.158195282000179
my_product2: True - 5.210837743999946
So I would go with the simplification of your original algorithm