Python Forum
question of using not in if statement and ..
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
question of using not in if statement and ..
#1
hi
in the below code:
"""
This is the "example" module.

The example module supplies one function, factorial().  For example,

>>> factorial(5)
120
"""

def factorial(n):
    """Return the factorial of n, an exact integer >= 0.

    >>> [factorial(n) for n in range(6)]
    [1, 1, 2, 6, 24, 120]
    >>> factorial(30)
    265252859812191058636308480000000
    >>> factorial(-1)
    Traceback (most recent call last):
        ...
    ValueError: n must be >= 0

    Factorials of floats are OK, but the float must be an exact integer:
    >>> factorial(30.1)
    Traceback (most recent call last):
        ...
    ValueError: n must be exact integer
    >>> factorial(30.0)
    265252859812191058636308480000000

    It must also not be ridiculously large:
    >>> factorial(1e100)
    Traceback (most recent call last):
        ...
    OverflowError: n too large
    """

    import math
    if not n >= 0:
        raise ValueError("n must be >= 0")
    if math.floor(n) != n:
        raise ValueError("n must be exact integer")
    if n+1 == n:  # catch a value like 1e300
        raise OverflowError("n too large")
    result = 1
    factor = 2
    while factor <= n:
        result *= factor
        factor += 1
    return result


if __name__ == "__main__":
    import doctest
in the above code,the line (if not n >= 0:):
why n<0 has been not used?namely, why it is not written as if n<0:?
is there any profit from using not?plz, explain.

in the line( if n+1 == n: ), for large value of n as 1e100, this is True and if block is runned.
why for a very large value of n, this condition is True? plz, explain.
in the above subject, what is the smallest value for n that the condition is satisfied? how the value is obtained?

last question:
What is the difference or profit of using not 3==2 with respect to (3 !=2) using in if statement?
thanks
Reply
#2
This is a very poorly written function. Let me first answer your questions:

I think the author wrote if not n >= 0 because he/she thought that it was easier to understand for the human reader. It is shorter to write if n < 0 and it works just as well.

The line if n + 1 == n is a terrible hack. It will work for floating points numbers because, due to limited floating point precision, if n is a large enough float, the expression n + 1 will return n. It means that the functions introduces an unknown limit on the size of n that depends on the implementation of the floating numbers. This is a terrible idea. Moreover, the idea does not work for integers because however large the integer n, Python will always return a different value for n + 1.

>>> n = 10 ** 1000
>>> n + 1 == n
False
>>> n = 1e300
>>> n + 1 == n
True
If that was the idea, it would be much better to write

if n >= 1e300:
    raise OverflowError('n too large')
Now let's do some mathematics: the factorial function is normally defined for nonnegative integers, which means that the function would be better written with an integer argument. There is a known extension of the factorial to the real numbers and it is called the gamma function. It even works for complex numbers except the negative integers. So a better definition would be

from scipy.special import gamma

def factorial(x):
    return gamma(x + 1)
Let's try it
>>> factorial(5)
120.0
>>> factorial(5.01)
122.06601197384288
>>> factorial(6)
720.0
>>> factorial(6+0.1j)
(706.8671115626526+133.95353752169385j)
>>> factorial(-1)
inf
>>> factorial(3.14)
7.173269190187901
akbarza likes this post
« We can solve any problem by introducing an extra level of indirection »
Reply
#3
(Feb-28-2024, 10:09 AM)Gribouillis Wrote: This is a very poorly written function. Let me first answer your questions:

I think the author wrote if not n >= 0 because he/she thought that it was easier to understand for the human reader. It is shorter to write if n < 0 and it works just as well.

The line if n + 1 == n is a terrible hack. It will work for floating points numbers because, due to limited floating point precision, if n is a large enough float, the expression n + 1 will return n. It means that the functions introduces an unknown limit on the size of n that depends on the implementation of the floating numbers. This is a terrible idea. Moreover, the idea does not work for integers because however large the integer n, Python will always return a different value for n + 1.

>>> n = 10 ** 1000
>>> n + 1 == n
False
>>> n = 1e300
>>> n + 1 == n
True
If that was the idea, it would be much better to write

if n >= 1e300:
    raise OverflowError('n too large')
Now let's do some mathematics: the factorial function is normally defined for nonnegative integers, which means that the function would be better written with an integer argument. There is a known extension of the factorial to the real numbers and it is called the gamma function. It even works for complex numbers except the negative integers. So a better definition would be

from scipy.special import gamma

def factorial(x):
    return gamma(x + 1)
Let's try it
>>> factorial(5)
120.0
>>> factorial(5.01)
122.06601197384288
>>> factorial(6)
720.0
>>> factorial(6+0.1j)
(706.8671115626526+133.95353752169385j)
>>> factorial(-1)
inf
>>> factorial(3.14)
7.173269190187901

thanks for reply
do you have any explanation about question 3? in question 2, I asked about the smallest value of n so that n==n+1 is true. do you have any opinion about it?
thanks again
Reply
#4
(Feb-29-2024, 06:28 AM)akbarza Wrote: do you have any explanation about question 3?
For integer numbers, there is no different in using if not 2 == 3 versus if 2 != 3. For objects, it could be different because a == b will invoke the __eq__() method while a != b invokes the __neq__() method. If classes overwrite __neq__(), the result could be different from the negation of ==.

(Feb-29-2024, 06:28 AM)akbarza Wrote: I asked about the smallest value of n so that n==n+1 is true.
I wrote a small program to find the value by bisection on my machine
from math import nextafter

def find_limit():
    a, b = 0, 1e300
    while b != b + 1:
        b = 2 * b

    while a < b:
        m = (a + b) / 2
        if b == m:
            break
        if m == m + 1:
            b = m
        else:
            a = m
    return a

x = find_limit()
print(x, 'x == x + 1?', x == x + 1)
x = nextafter(x, float('inf'))
print(x, 'x == x + 1?', x == x + 1)
Output:
1.008730964287089e+16 x == x + 1? False 1.0087309642870892e+16 x == x + 1? True
So the largest float value such that x != x + 1 is 1.008730964287089e+16 on my computer. Note that this is completely irrelevant for computing the factorial because factorial(10 ** 16) is an incredibly large number larger than (10**15)**(10**16) according to Stirling's formula.

By comparison, the largest float for which scipy computes a finite value for the gamma function is 171.62437695630268 on my machine, so you can safely ignore n larger than 200.
akbarza likes this post
« We can solve any problem by introducing an extra level of indirection »
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Yield statement question DPaul 6 2,528 Sep-26-2020, 05:18 PM
Last Post: DPaul
  SELECT statement query question using a variable DT2000 2 3,037 Feb-23-2019, 07:35 AM
Last Post: DT2000

Forum Jump:

User Panel Messages

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