mersenneforum.org  

Go Back   mersenneforum.org > Extra Stuff > Blogorrhea > Dylan14

Reply
 
Thread Tools
Old 2019-12-06, 23:49   #12
Dylan14
 
Dylan14's Avatar
 
"Dylan"
Mar 2017

24×3×11 Posts
Default Functions

So far we have dealt with some of the functions built into Python 3. We haven't touched all of the functions yet (we haven't dealt with for loops, the while function, slicing, file operations, etc.), but even with all of these functions, we are somewhat limited in what we can do with them. So, to supplement Python's abilities, we can do the following things:
a) Define functions that do a specific thing.
b) Import modules that extend the capabilities of the code.
Now modules may consist of many classes(*), each of which may have one or more functions. Hence, we will use this section to discuss about functions, how one may write a function, and a few examples.

Functions:

A function in Python isn't exactly like a function in mathematics (where you take a number, perform something on that number, and then get another number back). Rather, it's a block of organized code that performs a single action. There are two types of functions:
a) Built in functions - which we have been exclusively using so far
b) User defined functions
Now, the syntax for a function is the following:
Code:
def name_of_function(args):
     "doc string"
     code that the function should run
     return something
Let's go through each of these items in turn:
* All functions must start with the keyword "def". This is then followed by the function name, and then a set of parentheses and a colon after the parentheses.
* Within the parentheses, we include all of the arguments that the function is allowed to take. It is possible to have a function that takes no arguments. We will cover the types of arguments in a moment.
* After the colon, an indented block starts. The next thing, which is optional, is the doc string. Usually this is encased in triple quotes. Also, this should hopefully tell the user what the function does.
* After the doc string comes the meat of the function. This is where the code that the function should run goes. This can be very simple, or it can involve very complex things with multiple loops.
* Once done, the function must include a return expression. The return expression may have 0 or more arguments. If it has 0 arguments, it is interpreted as return None.

Let's do a simple example now, before we move to recursive functions and a more detailed view of arguments.
Sum function:
This function takes two integers, a and b, and returns the sum of the two:
Code:
def sum(a, b):
     return a+b
Now if we type
Code:
a = 1
b = 3
sum(a, b)
and execute in a new cell, we get 4, as we expect.

Recursive functions, and an example:

A recursive function is a function whose value is based on a previous value of the function. A couple of examples of these include:
* The Fibonacci sequence (0th term is 0, first term is 1, nth term is the sum of the previous two terms)
* The Lucas sequence (similar to the Fibonacci sequence but the first two terms are 2 and 1)
* The factorial function (0! = 1, 1! = 1, 2 = 2*1 = 2*1!, 3 = 3*2*1 = 3*2!, etc., so that n! = n*(n-1)!)
In Python, recursion can be done, by calling the function within the function with a different argument(**). To see this in action, let's code up the factorial function:
Code:
def factorial(n):
     #Computes the factorial of a positive integer
     if n < 0:
          raise ValueError("n must be zero or a positive integer")
     elif n == 0:
          return 1
     elif n == 1:
          return 1
     else:
          return n*factorial(n-1)  #see the recursion!
Try this on the following n's:
0 - should get 1
1 - should get 1
3 - should get 6
.2 - should get an error as after the first recursion, n-1 = -.8 which is smaller than 0.

More on arguments:
As stated above, we stated that we have arguments within the parentheses for functions. There are 4 types of arguments:
*Required arguments
*Keyword arguments
*Default arguments
*Variable length arguments

A required argument must be placed in correct positional order. The number of arguments in the function call must match exactly with the way we defined it.
Going back to our function sum(a, b), we see that the sum function must take 2 arguments when we call it. If we don't have that many arguments, then we get an error.
If we go back to the cell that we defined a and b and we remove the b in the sum, we get the following:
Code:
TypeError: sum() missing 1 required positional argument: 'b'
Keyword arguments are related to the function call. When a keyword argument is used, the interpreter will identify the argument by their name. As such, we can put them out of order.
For example, in a cell after you ran the cell with the sum definition, run this:
Code:
sum(b= 7, a = 2)
this should yield 9.
Default arguments: These specify a default value for an argument that will be used if the argument is skipped.
Variable length arguments:
In some functions, you may find it useful to have a function that takes more arguments than you defined. These are known as a variable length argument, and these are not named in the function definition, like so:


Code:
def name_of_function([args], *var_args_tuple):
     "doc string"
     code that the function should run
     return something
the * before a variable denotes a variable that holds all the nonkeyword variable arguments.
Lambda:
Lambda can be used to run an expression, but not a command or multiple expressions. I haven't used these, but they may prove useful to you.
The syntax is the following:

Code:
lambda [args]:expression
where args is a comma separated list of arguments. There can be as many arguments as you like.
Lambda is also limited to using only variables that are declared globally, declared within the class (if it is a class) or those in the parameter list.

(*) Classes are used to define an object and the properties of all objects that are in the class. This has a clear analogue to C/C++ classes. As this requires more things to come together, we will cover this later.
(**) Care must be taken that you don't write a function that doesn't terminate.

Last fiddled with by Dylan14 on 2019-12-25 at 00:53 Reason: 0! = 1
Dylan14 is online now   Reply With Quote
Old 2019-12-21, 20:18   #13
Dylan14
 
Dylan14's Avatar
 
"Dylan"
Mar 2017

24·3·11 Posts
Default Modules and packages

So we have mentioned that functions and classes are the backbone of modules, which are used to extend the abilities of Python. So what exactly are modules, and how can we add some to Python?
A module is an object which is a organizational unit of Python code. Each of these have a namespace which contains other objects (such as functions, variables and other objects). These can be used within a Python program, but they first must be imported first. Related to modules are packages, which is actually a special type of module that can contain submodules, or subpackages (*). To use these, one must first download the package and then import the necessary modules within the package.

Getting and installing modules/packages:

To install a module or package, one must first ensure that pip is installed in your Python distribution. This shouldn't be a problem as the main installer of Python (see post 3, How to Get/Use Python) includes this. If you are using a distribution of Python, it may include a package manager which one can use to install packages. For example, the Anaconda distribution of Python has a utility called Anaconda navigator, which provides a GUI for package installation/upgrading. If you are using a Linux distro version of Python, you may need to install pip. This can be done in Ubuntu and similar flavors as follows (entering your password if needed):

Code:
sudo apt-get install python3-pip
Once you have pip installed, you can then go to the Python Package Index (https://pypi.org/) and search for packages. When you have found a package, you can install it by typing the following on a command line:

Code:
python -m pip install NameofPackage
or on Linux (**)(***)

Code:
pip install NameofPackage
(****)

This will yield the following, for example:

Code:
dylan@ubuntu:~/Desktop$ python3 -m pip install numpy
Collecting numpy
  Downloading https://files.pythonhosted.org/packages/9b/af/4fc72f9d38e43b092e91e5b8cb9956d25b2e3ff8c75aed95df5569e4734e/numpy-1.17.4-cp37-cp37m-manylinux1_x86_64.whl (20.0MB)
    100% |████████████████████████████████| 20.0MB 58kB/s 
Installing collected packages: numpy
  The scripts f2py, f2py3 and f2py3.7 are installed in '/home/dylan/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
Successfully installed numpy-1.17.4
If you want to upgrade a package, you can do so using the following:
Code:
pip install --upgrade NameofPackage
and you can also remove packages using the --uninstall option. Further information on pip syntax can be found at https://pip.pypa.io/en/latest/reference/.

Importing and using a module

Once you have downloaded a package, we can then import it into our program, so that the objects that are in the package/module within the package can be called(*****). There are three ways to do this

1. import ModuleName
2. import ModuleName as ShortName
3. from ModuleName import "comma separated list of functions, or *"

1. This is the simplest way to import a module, as it will give you all the functions in one go. The downside is that you have to call the full name of the module every time you want to use something from it (i.e. ModuleName."chain of submodules, period separated".function(args))
2. With this, you can shorten the length of calls to functions. You have to preface a call to a function within a module with "ShortName.".
3. With this, you can select the functions that you specifically want, or you can use the asterisk to import everything in the module. The upside is that you don't need to preface a function call with anything before calling the function. The downside is that you may have conflicting function definitions. Think of this like the struct "using namespace std" in C/C++.
Note that the as function can also be used to rename functions when importing, provided you are doing one function at a time (i.e. from Module import Function as FunctionName, in generic terms).

An example:

Let's try to compute the matrix inverse of the following matrix:
\begin{bmatrix}<br />
1 & 2\\<br />
3 & 4<br />
 \end{bmatrix}

To do this, we can use the inv function in numpy.linalg(6). We'll show how to call this in the 3 ways we did above.

1. Using import only:
Code:
import numpy
a = numpy.array([[1.,2.],[3.,4.]])
ainv = numpy.linalg.inv(a)
ainv
2. Using import with as:
Code:
import numpy as np
a = np.array([[1.,2.],[3.,4.]])
ainv = np.linalg.inv(a)
ainv
3. Using the from command (with the as command as well):
Code:
import numpy as np
from numpy.linalg import inv
a = np.array([[1.,2.],[3.,4.]])
ainv = inv(a)
ainv
All of these yield the same thing:
Code:
array([[-2. ,  1. ],
       [ 1.5, -0.5]])
or in fancy LaTex:
\begin{bmatrix}<br />
  -2 & 1\\<br />
  1.5 & -0.5<br />
 \end{bmatrix}

(*) Actually, a package is any module that has the __path__ attribute.
(**) Some packages may be available in the distro repository. Try, for example,
Code:
sudo apt-get install python3-numpy
to install numpy in Ubuntu or Debian.
(***) In some other distros packages might already be included when you install. An example is ArcoLinux, which by default on a fresh install has numpy installed.
(****) When you execute this command, pip may install additional packages that the package you selected needs in order to run. These are known as dependencies.
(*****) If the module that you want to use is on the list provided here, you don't need to download it as Python includes these by default. If not, you will need to download the package first before you import, otherwise you will get a ModuleNotFoundError.
(6) We'll cover numpy in more detail later.

Last fiddled with by Dylan14 on 2020-06-23 at 15:32 Reason: add additional note on package installation
Dylan14 is online now   Reply With Quote
Old 2019-12-25, 00:52   #14
Dylan14
 
Dylan14's Avatar
 
"Dylan"
Mar 2017

21016 Posts
Default For loops part 1: Introduction to for loops

So far, we covered one part of the backbone of programs: the if/else/elif statements. Now we move to loops. A loop (in coding lingo) is a sequence of statements which is specified once, but can be run many times, even an infinite number of times. There are several types of loops:
* for loops (run a piece of code x number of times, and we keep track of how many times we run the code with a counter)
* while loops (while some boolean statement is true, run a piece of code and break out when the statement is no longer true)
* for each loops (run a piece of code on every single item in a collection of objects)
* do...while loops (run a block of code at least once, and then repeat the block depending on the truth value of a statement at the end of the block)
For the next couple of sections, we will deal with the for loop.

For loop syntax:
The for loop in Python has the following syntax:
Code:
for <variable> in <iterable object>:
      do something
Here, <variable> is known as the iterator. The iterable object can be a list, a string, a tuple, or any object which can be iterated over(*). After this, we follow with a colon and then an indented block starts. Inside the indented block is code that should be iterated over, through all the objects in the iterable object.
Some notes here:
1. The object that we are iterating over must be iterable. You can't iterate over an integer, otherwise you will get a error.
2. The indented block must have something, it can't be empty. Like with the if/else construct, you can use the pass function if you don't want to run anything inside the loop.
3. You can have nested for loops. In this case the inner-most loop will iterate through, then (if you've coded things correctly) the next inner loop will iterate and the innermost one will reset, and so on.
A simple example:

Let us print out the exponents that are known to produce Mersenne Primes (which is used in several programs, like dmdsieve). We can do this using a for loop:
Code:
exp_list = ['2', '3', '5', '7', '13', '17', '19', '31', '61', '89', '107', '127', '521', '607', '1279', '2203', '2281', '3217', '4253', '4423', '9689', '9941', '11213', '19937', '21701', '23209', '44497', '86243', '110503', '132049', '216091', '756839', '859433', '1257787', '1398269', '2976221', '3021377', '6972593', '13466917', '20996011', '24036583', '25964951', '30402457', '32582657', '37156667', '42643801', '43112609', '57885161', '74207281', '77232917', '82589933']
for exp in exp_list:
     print(exp)
Here, we are iterating over a list called exp_list, and our variable is called exp (**). For each item in the list, we print it.
This is an example of a for each loop, even though in Python we are simply using for and in.

The range function:

Say we want to run a loop a certain number of times. To do this, we can use the range function. The syntax of the function is as follows:

Code:
range(start, stop, step)
The function requires one argument, stop, and start and step are optional. The arguments do the following things:
start - specifies the start of the sequence. By default, this is 0.
stop - a required argument, which specifies the upper bound of the sequence. This number is not included in the range.
step - specifies the difference between each result. By default, this is 1.
A few notes:
Start, stop and step must be integers.
The arguments can be negative or positive.
Step has to be non-zero, otherwise you will get a ValueError.
A simple example with range:
Code:
x = range(6)
for i in x:
     print(i)
which yields:
Code:
0
1
2
3
4
5
so you can see that 6 is not included.

Slightly more complicated example with range, with start and step
Code:
y = range(-1,12,3)
for i in y:
     print(i)
which yields:
Code:
-1
2
5
8
11
The next increment, which would be 14, is greater than 12 and therefore is not printed.
There are similar functions included in numpy called arange and linspace which deal with arrays. We will deal with these later when we mention numpy.

Factorial, revisited:
Recall that when we covered functions we defined the factorial function, using if, else and elif. We can do a similar thing with for loops, with a few if's to cover some of the unit cases (***):
Code:
def factorial(n):
    fac = 1
    if n < 0:
        raise ValueError("Factorial is defined for positive integers.")
    if n == 0:
        return fac
    for i in range(n,0,-1):
        fac = fac*i
    return fac
Note here we use all the arguments of the range function. As a test, try the following inputs:
0 - should get 1
1 - should get 1
3 - should get 6
.2 - should get a TypeError

(*) Most things in Python can be made iterable (even functions!). We will deal with this (as well as the iter function) later.
(**) Actually, it doesn't matter what I call the iterator. I just need to note that whatever I call it, I must use that throughout that code block.
(***) An important thing to do when testing code is to run it on some unit cases. For example, for the factorial function, we should test it on 0, since 0! is defined to be one.

Last fiddled with by Dylan14 on 2019-12-25 at 00:57 Reason: factorials were first mentioned in functions
Dylan14 is online now   Reply With Quote
Old 2020-01-09, 02:13   #15
Dylan14
 
Dylan14's Avatar
 
"Dylan"
Mar 2017

24·3·11 Posts
Default For loops part 2: The break and continue commands

So far, we just dealt with how for loops work on a high level, and we constructed a few ways to set up a loop (namely, using either an iterable object or a range object). But say we want to control a loop more succinctly, and in particular, we want to have a way to "escape". This can be done using the break and continue commands. We will apply these to an example that is very prevalent at mersenneforum: trial factoring of Mersenne numbers.

The break command:

Break is a very simple command: it breaks out of a loop (which can be a for loop or a while loop (*)). The syntax is as follows:

Code:
break
In psuedocode, this would look like this:

Code:
#stuff before the loop
for object in a sequence:
    #stuff to run in the loop
    if condition:
        break
        #then jump out the loop and then go to the next piece of code outside the loop
    #stuff following the break statement

#stuff after the loop
If one has a nested for loop, then a break will terminate the loop in which it appears, and move to the next iteration of the next inner loop.
Example of a break statement - one loop:
Code:
string = "mersenneforum"
for i in string:
    if i != "s":
        print(i)
    else:
        break
What this does is checks if a character in the string is not s. If it is not s, we print the character. Otherwise, we break out of the loop, and since nothing follows, execution stops.
Nested loops and break:
Code:
characters = "abcdefghijklmnopqrstuvwxyz"
for i in range(6):
     print("Outer loop iteration:" + str(i))
     for j in characters:
         print(j)
         if j == "c":
              print("Inner loop interrupted...")
              break
This code runs the outer loop, which prints the current iteration, and then the inner loop runs, which prints the alphabet up to c, and at that point the inner loop breaks, and we go to the next iteration of the outer loop.

The continue command:

The continue command is similar to the break command, but instead of leaving the loop, we skip whatever is left in the body of the loop and start the next iteration.
In psuedocode:
Code:
#stuff before the loop
for object in a sequence:
    #stuff to run in the loop
    if condition:
        continue
        #then go to the beginning of the code block in the loop
    #stuff after the continue statement
#stuff after the loop
The syntax is the same as with break.
Example with continue:
Code:
string = "mersenneforum"
for i in string:
    if i != "n":
        print(i)
    else:
        continue
Here, we check if the character is not n, and if it is not, we print the character. Otherwise, we go to the next letter in the string. The result is that we get all the characters in mersenneforum, one character at a time, except for the two n's in that.

An application: Mersenne trial factoring:
We can use for loops, the continue statement, and the break statement to create a simple program to trial factor Mersenne numbers (**).
First, some facts that we can use:
1. Factors of Mersennes must have the form 2*k*p+1, where p is the exponent, which is a prime number.
2. The factor must be 1 or 7 (mod 8). This reduces the search space quite a bit.
So how can we implement this? Well, the first statement tells us that we could iterate through all k's from some lower bound to some upper bound, via a for loop.
The second statement states that we should check if q = 2*k*p+1 is 1 or 7 mod 8 and if it isn't, discard it.
So we can code this:
Code:
#A simple trial factor program for Mersennes
p = input("Enter a prime number: ")
mer = 2**int(p)-1
mink = 1
maxk = 10000000
for k in range(mink, maxk+1): #test all k's from mink to maxk
  #candidate factor is in the form 2*k*p+1
  candidate = 2*k*int(p)+1
  #check if the candidate is 1 or 7 (mod 8)
  if candidate%8 == 1 or candidate%8 == 7:
    #divide the mersenne number by the candidate and see if it factors it
    if mer%candidate == 0: #the candidate divides the mersenne
      print("M" + str(p) + " has a factor: " + str(candidate))
      break
  else: 
    #if not, go to the next k
    continue
This code first requests an exponent from the user. The program then generates the Mersenne number corresponding to that exponent, and then for all k's running from kmin to kmax, it first creates the corresponding candidate factor, tests if it is 1 or 7 mod 8 and if it is, checks if it divides the mersenne. If it does, it prints the factor to the screen and breaks. If not, it continues until it finds a candidate that works, or it reaches the end of the range.
Test on a known factor:
Code:
Enter a prime number: 7112003
M7112003 has a factor: 355600151
corresponding to k = 25 (see https://www.mersenne.ca/exponent/7112003).

(*) We will cover while loops in the next section.
(**) Note I say simple, not fast. If you want speed, I recommend either using prime95/mprime if you have an x86_64 processor, mfactor from the mlucas suite if you have an ARM processor, factor5 for higher Mersenne's, mfaktc/mfakto for GPU's, or mmff, dm or dmdsieve + pfgw/LLR for Double Mersennes.
Dylan14 is online now   Reply With Quote
Old 2020-01-10, 01:53   #16
Dylan14
 
Dylan14's Avatar
 
"Dylan"
Mar 2017

24·3·11 Posts
Default While loops

In the last two sections, we covered for loops, which allows us to run a loop for a certain number of times. But say we want to run a piece of code if some condition is true. To do this, we can use a construct called the while loop.

The while loop:

The syntax of the while loop is as follows:
Code:
while [a condition is true]:
     do something
The condition must be a boolean statement, or it can be the elements True or False. If a boolean statement is given, then the program will evaluate the boolean statement and only evaluate the body of the loop if it evaluates True. If the element True is encountered, then the code block will continue forever (*). If the element False is encountered, then the code will not execute, as False is never True.
A simple example:
Let's sum the first ten positive integers. We can do this in a while loop:
Code:
sum = 0
i = 1
while i<=10:
  sum = sum + i
  i += 1
print(sum)
So, we first initialize a variable sum to 0 and i to 1. While i is less then or equal to 10, we add i to sum, and increment sum. Internally, sum goes as follows: 1, 3, 6, 10, 15, 21, 28, 36, 45, 55. Once we ran through the loop 10 times, i is 11. But 11 > 10, so we skip the loop, and then print the sum, which is 55.

Infinite loops:

An infinite loop occurs in a while loop only, since as we recall, for loops only run until the iterable object is exhausted. We can create an infinite loop by creating a condition that cannot ever be false, for example:

Code:
while(True):
     do something
Of course, you should be careful with what you put into such a loop, as you don't want to end up running out of memory because you are trying to enumerate all the primes in a loop (**). There is a couple of things that this construct would be useful:
1. You are trying to monitor something on your computer, like CPU usage or GPU usage (***).
2. You are running a server and you want the server to run continuously so that clients can attach to it, communicate, and then detach at will.
3. You are running a DC project which can be left running for long periods of time.

Now, of course we can use break and continue to control while loops, which can be useful in infinite loops(****).
Example - Pascal's triangle
Pascal's triangle is a triangular array of the binomial coefficients, which are defined as follows:

{n\choose k} = \frac{n!}{k!(n-k)!}

Using while loops, we can compute the rows forever, however, using break, we will terminate after row 5 (*****):
Code:
i = 0
while(True):
  print("Row " + str(i+1))
  for j in range(i+1):
    entry = int((factorial(i))/(factorial(j)*factorial(i-j)))
    print(str(entry))
  i += 1
  if i > 4:
     break
This yields each of the first 5 rows of Pascal's triangle. Of course, we can change the number of rows that we print out by changing the number in the second to last line in the above code snippet (6).
Alternatively, if we want to do the same thing without break, this works:
Code:
i = 0
while(i <= 4):
  print("Row " + str(i+1))
  for j in range(i+1):
    entry = int((factorial(i))/(factorial(j)*factorial(i-j)))
    print(str(entry))
  i += 1
What about do... while loops?

When I mentioned loops in the first part of the for loop discussion, we mentioned that there is a thing called do while loops, where you do something at least once, and then you repeat if the truth value of the statement is true. Now Python does not have this construct (in stark contrast to C, C++, and Java), but you could emulate this. Here's a possible way to do this:
Code:
i = 5
while(True):
     #do something
     i -= 1
     if i == 0:
          break
Basically, we run something (the do statement), and then we decrement i, and then we run again (the "while" statement is true) until i = 0, and then we stop (the "while" statement is false).

(*) Well, forever in the sense that the code will continue to run until either one of the following happens:
1. There is an error that is encountered, which automatically stops execution,
2. Ctrl+C is pressed, which is the keyboard interrupt,
3. Some other thing happens which causes the code to stop executing (Windows update, BSOD, kernel panic, power outage, time limit exceeded in a Colab/Kaggle session, etc.).
(**) I wish you good luck if you want to do that.
(***) This is possible, see for example the gpustat package on PyPi.
(****) There are other things that we can use; for example, the command sys.exit(). We will cover the sys module later.
(*****) You will need to use the factorial function that I created, either in Part 1 of for loops, or in the section on functions.
(6) Just don't delete the last 2 lines, otherwise, you will not stop.

Last fiddled with by Dylan14 on 2020-01-12 at 00:20 Reason: add another method to compute Pascal's triangle. Same computationally, no break.
Dylan14 is online now   Reply With Quote
Old 2020-01-12, 01:44   #17
Dylan14
 
Dylan14's Avatar
 
"Dylan"
Mar 2017

24×3×11 Posts
Default Putting it all together

Now that we have learned about if, else, elif, for and while, we can go on and try to work with all of these to build programs. We'll do a few examples, one of which is an OEIS sequence.

Example: OEIS A003599:

OEIS A003599 lists all numbers of the form 7^i*11^j for i and j non negative. The terms are given as 1, 7, 11, 49, 77, 121, etc., corresponding to the (i, j) values of (0,0), (1,0), (0,1), (2,0), (1,1), (2, 0), respectively. The listings goes up to 5764801, which is 7^8*11^0. Now, how can we code this?
1. We need two variables, i and j. They should both start at 0.
2. Since the limit of the sequence shown on the page is 5764801 (*), we should limit ourselves to, say, 6000000.
So we could first use a for loop to loop over i, and then while we are under the limit, we print i, j, and the resulting number.
So let's code it.
First, we should define a function to compute the numbers:
Code:
def numform(i, j):
     #computes 7^i*11^j
     return (7**i)*(11**j)
Next, we develop the main loop: we iterate over i in a for loop, and then we test if 7^i*11^j is smaller than our bound. If it is, we then print the tuple (i, j), and the resulting number, and then we increment j. Otherwise, we set j = 0, and increment i. This is coded as follows:
Code:
j = 0
for i in range(0,11): #test all i's from 0 to 10
  #if 7^i*11^j is smaller than a certain bound (namely, 6000000), print i, j and the resulting number
  while numform(i,j) <= 6000000:
    print("(" + str(i) + ", " + str(j) + "), " + str(numform(i,j)))
    j += 1
  else:
    j = 0
When we do this, we get the following output:
Code:
(0, 0), 1
(0, 1), 11
(0, 2), 121
(0, 3), 1331
(0, 4), 14641
(0, 5), 161051
(0, 6), 1771561
(1, 0), 7
(1, 1), 77
(1, 2), 847
(1, 3), 9317
(1, 4), 102487
(1, 5), 1127357
...
which does correspond to the entries in A003599.
By changing the appropriate things in the code, you can recreate the following sequences in OEIS: A003591-A003599. I'll leave it as an exercise to do that.

Using else in for and while loops:

In our code for A003599, we used else after a while loop, which we would usually use after an if statement. But we were able to use else after a while loop, within a for loop in Python. This is actually unique to Python. The following happens when you use else with a loop:
* If else follows after a for loop, then the code that is in the else block runs once the code exhausts the iterable object that the for loop is operating on.
* If else follows after a while loop, then the code that is in the else block runs once the condition that governs the while loop becomes false.
To see this in action, let's go back to the code we wrote for A003599. With the code that we used and the bounds that we set, we get results all the way up to i = 8. If, instead, we excluded the else block, we get the following output:

Code:
(0, 0), 1
(0, 1), 11
(0, 2), 121
(0, 3), 1331
(0, 4), 14641
(0, 5), 161051
(0, 6), 1771561
so we only get the lines with i = 0, as then j is not reduced afterwards from 7. Then, clearly 11^7 = 19487171 is larger than our bound of 6000000, so we increment i. But when we increment i, we will get an even bigger number. So we never get another number that is smaller than our bound, so the output stops.

Example: Logging in (**)

Many websites require you to log in in order to get use all the functionality of the site. Such logins usually involve a username and a password (and occasionally some two factor authentication). Sometimes sites (like Mersenneforum) will only allow you to try x number of times before locking you out for a short period of time. How can we implement this?
1. We set up a password (here we will use a plain string). Usually, it is encrypted and salted on the server. However this is beyond the scope of this section.
2. While we have not exceeded the number of attempts, we ask the user to supply the password.
3. If it is correct, then we break out of the loop.
4. If it is incorrect, then we tell the user "wrong password, try again", and increment the attempts by 1.
5. If the maximum number of attempts have been made, then we lock the user out (here we'll just print a message).
Here's the code:
Code:
#Passwords and logging in
password = "passw0rd"
i = 1
while i <= 3: #3 attempts
  #ask for input
  inputtedpassword = input("Enter your password: ")
  if inputtedpassword == password:
    #log in
    print("Logging in...")
    break
  else:
    #warn user, ask them to try again
    print("Wrong password, try again")
    i += 1
else:
  #lock user out
  print("You have been locked out. Try again later")
This does work, but I would advise not using this code for your website. It is meant to be a proof of concept (***)

(*) The b-file goes a lot higher than this, but we want to just recreate the terms on the main sequence page.
(**) This checks against a plain string, which is in the code. Needless to say, you should NEVER have passwords in plain sight (for obvious reasons).
(***) Python does have a getpass module, which enables the password to be hidden when typing it in. That is the least of my concerns with using this (see point (**) above).
Dylan14 is online now   Reply With Quote
Old 2020-07-19, 21:46   #18
Dylan14
 
Dylan14's Avatar
 
"Dylan"
Mar 2017

24·3·11 Posts
Default Strings part 2 - indexing, repeating, and slicing

So far, we have dealt with the concept of strings, the fact that we can concatenate them, and using the format command to change how the string appears when outputted. But there are other things we can do with strings:
1. We can index the characters within a string with an index.
2. We can repeat a string multiple times.
3. We can slice a string, to extract only a certain part of the whole string.
There are other useful things, which I will mention briefly in the next section. More details can be found in the Python documentation, or elsewhere.
But first, let's revisit multiline strings:

Multiline strings:

Recall when I wrote about multiple line comments, I had an example as follows:
Code:
"""
Some comments need 
more than one line, 
like this one
 """
This is actually a valid string, as can be evidenced when I type this into an interpreter:
Code:
>>> msg = """Some comments need
more than one line,
 like this one
"""
 >>> print(msg)
Some comments need
more than one line,
like this one
In general, to do a multiline string like this, you would need to encase the 'stuff' in either triple double quotes ("""stuff""") or triple single quotes ('''stuff'''). Alternatively, you could use a single line string, and the escape sequence '\n' to start a new line, as follows:

Code:
>>> msg2 = "The rain in Spain \nfalls mainly on the plain."
>>> print(msg2)
The rain in Spain 
 falls mainly on the plain.
Indexing and slicing strings:

Like other languages, strings are arrays of bytes representing characters.

Unlike other languages, such as C, Python does not have a character data type (*). Instead, characters are just strings with length one.
Since the string is an array, I can index the elements of the string. A couple of notes:
1. Indexing starts from 0, which denotes the first character.
2. As a corollary of this, you can only go to the index n-1, which is the last character in the string. If you go beyond this, you will get an IndexError.
3. You can use negative indices. In this case you will index from the last character in the string, and work backwards. Here the furthest back you can go is -n, which is the first character in the string.

Examples:

Let's consider the following single line string:
Code:
The rain in Spain falls mainly on the plain.
To ensure that we don't get an IndexError, we will use the len command, which gives the length of the string.
Code:
>>> msg3 = "The rain in Spain falls mainly on the plain."
>>> len(msg3)
44
With this, the valid indices are -44 to 43.
1. Let's pick out the S in Spain. We count from 0, which is the T in the. 1 - h, 2 - e, 3 - space, 4 - r, 5 - a, 6 - i, 7 - n, 8 - space, 9 - i, 10 - n, 11 - space, 12 - S. So msg3[12] should be S. Let's check:
Code:
>>> print(msg3[12])
S
2. Alternatively, let's go backwards. -1 - ., -2 - n, -3 - i, ..., -32 - S. So msg3[-32] should also be S. Let's check:
Code:
>>> print(msg3[-32])
S
3. What happens if we try index 50?
Code:
>>> print(msg3[50])
Traceback (most recent call last):
  File "<pyshell#18>", line 1, in <module>
    print(msg3[50])
IndexError: string index out of range
We get an error, as 50 > 43.

Slicing a string:
Now, we will use indices to get a piece of the string. Slicing uses colons, and have three arguments, all of which have to be integers.
1. The first number denotes the initial index.
2. The second number denotes the final index.
3. The third number denotes the step. This can't be 0, and by default is 1 (meaning, print all characters between the initial index and the final index).
If you have at least one argument, the rest are optional. This goes as follows:
By default, the first argument is 0 (start at the beginning of the string), the second argument is len(string) - 1 (the end of the string) and the third argument is 1.

Examples: Again, let's consider the following string:
Code:
The rain in Spain falls mainly on the plain.
1. Let's take the 7th through 15th characters in the string:
Code:
>>> msg4 = "The rain in Spain falls mainly on the plain."
>>> print(msg4[6:14])
 in in Sp
2. Let's print the 5th character from the end to the end of the string:
Code:
>>> print(msg4[-5:])
lain.
3. Let's print every third character of the string:
Code:
>>> print(msg4[::3])
 T iiSiflmn  eln
Here, the final index that's printed in 42. The next jump, 45, is outside the range.

Repeating a string:
One can "multiply" a string, or more correctly, repeat a string, by using the * symbol. The syntax looks like the following:
Code:
 int * "stuff"
where 'int' is a positive integer and "stuff" is a string (**).

Example:

The usual battle of people in Misc. Math here goes as follows:
1. Some crank spews some garbage.
2. The intelligent people in the forum debunks said garbage.
3. The cycle repeats until the thread is locked or the crank leaves.

Let's describe it in a bit of Python:

Code:
>>> msg5 = "garbage, debunk, "
>>> print(3 * msg5 + "then the thread ends.")
garbage, debunk, garbage, debunk, garbage, debunk, then the thread ends.
(*) The module ctypes can be imported to give Python a C compatible char data type. But by default Python does not have a character data type.
(**) Actually, you can use a negative integer or 0 for the integer. But nothing will print.

Last fiddled with by Dylan14 on 2020-10-25 at 02:21 Reason: index 6 = 7th character! and similarly for index 14
Dylan14 is online now   Reply With Quote
Old 2020-07-19, 21:46   #19
Dylan14
 
Dylan14's Avatar
 
"Dylan"
Mar 2017

24·3·11 Posts
Default Miscellaneous string operations

(placeholder)

Last fiddled with by Dylan14 on 2020-10-24 at 18:10
Dylan14 is online now   Reply With Quote
Old 2020-10-24, 18:10   #20
Dylan14
 
Dylan14's Avatar
 
"Dylan"
Mar 2017

24×3×11 Posts
Default Introduction to lists

(placeholder)
Dylan14 is online now   Reply With Quote
Reply

Thread Tools


Similar Threads
Thread Thread Starter Forum Replies Last Post
Guide to compiling YAFU Mr. Odd YAFU 4 2017-04-24 15:40
Overclock Testing Guide? Caribou007 Software 18 2013-06-18 19:57
Guide OmbooHankvald Operation Billion Digits 4 2009-08-26 08:18
guide me about linux tinner Linux 23 2009-06-23 02:39
Guide OmbooHankvald 3*2^n-1 Search 3 2005-07-13 21:03

All times are UTC. The time now is 05:52.

Wed Nov 25 05:52:31 UTC 2020 up 76 days, 3:03, 4 users, load averages: 1.86, 1.68, 1.56

Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.

This forum has received and complied with 0 (zero) government requests for information.

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation.
A copy of the license is included in the FAQ.