diff --git a/docs/404.html b/docs/404.html index 0b178d8..9e3c57b 100644 --- a/docs/404.html +++ b/docs/404.html @@ -26,6 +26,14 @@ + + + + + + + + @@ -452,6 +460,27 @@ +
  • + + + + + Coding guide + + + + +
  • + + + + + + + + + +
  • diff --git a/docs/ascending.png b/docs/ascending.png deleted file mode 100644 index 1ae9c75..0000000 Binary files a/docs/ascending.png and /dev/null differ diff --git a/docs/coding/index.html b/docs/coding/index.html new file mode 100644 index 0000000..e3e96c6 --- /dev/null +++ b/docs/coding/index.html @@ -0,0 +1,2927 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Coding guide - Algorithmic Essays + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + Skip to content + + +
    +
    + +
    + + + + + + +
    + + +
    + +
    + + + + + + +
    +
    + + + +
    +
    +
    + + + + + +
    +
    +
    + + + + + + + +
    +
    + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + + + + +
    +
    + + + +
    + +
    + + + +
    +
    +
    +
    + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/deepnote-background/index.html b/docs/deepnote-background/index.html index 4af1d7d..03a16a8 100644 --- a/docs/deepnote-background/index.html +++ b/docs/deepnote-background/index.html @@ -32,6 +32,14 @@ + + + + + + + + @@ -463,6 +471,27 @@ +
  • + + + + + Coding guide + + + + +
  • + + + + + + + + + +
  • diff --git a/docs/deepnote-how-to/index.html b/docs/deepnote-how-to/index.html index 2b0558a..b9296e5 100644 --- a/docs/deepnote-how-to/index.html +++ b/docs/deepnote-how-to/index.html @@ -32,6 +32,14 @@ + + + + + + + + @@ -463,6 +471,27 @@ +
  • + + + + + Coding guide + + + + +
  • + + + + + + + + + +
  • diff --git a/docs/deepnote-reference/index.html b/docs/deepnote-reference/index.html index 1258379..3006ce4 100644 --- a/docs/deepnote-reference/index.html +++ b/docs/deepnote-reference/index.html @@ -30,6 +30,14 @@ + + + + + + + + @@ -461,6 +469,27 @@ +
  • + + + + + Coding guide + + + + +
  • + + + + + + + + + +
  • diff --git a/docs/deepnote/index.html b/docs/deepnote/index.html index 5bb5033..b4a52a9 100644 --- a/docs/deepnote/index.html +++ b/docs/deepnote/index.html @@ -32,6 +32,14 @@ + + + + + + + + @@ -463,6 +471,27 @@ +
  • + + + + + Coding guide + + + + +
  • + + + + + + + + + +
  • diff --git a/docs/essays/example-1-to-n/index.html b/docs/essays/example-1-to-n/index.html index 245e790..3205c1b 100644 --- a/docs/essays/example-1-to-n/index.html +++ b/docs/essays/example-1-to-n/index.html @@ -28,6 +28,14 @@ + + + + + + + + @@ -459,6 +467,27 @@ +
  • + + + + + Coding guide + + + + +
  • + + + + + + + + + +
  • diff --git a/docs/essays/example-jewels/index.html b/docs/essays/example-jewels/index.html index 6792aad..8d4f0ec 100644 --- a/docs/essays/example-jewels/index.html +++ b/docs/essays/example-jewels/index.html @@ -28,6 +28,14 @@ + + + + + + + + @@ -459,6 +467,27 @@ +
  • + + + + + Coding guide + + + + +
  • + + + + + + + + + +
  • diff --git a/docs/essays/example-two-sum-2/index.html b/docs/essays/example-two-sum-2/index.html index bc3e97f..0a60e32 100644 --- a/docs/essays/example-two-sum-2/index.html +++ b/docs/essays/example-two-sum-2/index.html @@ -28,6 +28,14 @@ + + + + + + + + @@ -459,6 +467,27 @@ +
  • + + + + + Coding guide + + + + +
  • + + + + + + + + + +
  • diff --git a/docs/essays/example-two-sum-3/index.html b/docs/essays/example-two-sum-3/index.html index 1e2d94b..1b6d817 100644 --- a/docs/essays/example-two-sum-3/index.html +++ b/docs/essays/example-two-sum-3/index.html @@ -28,6 +28,14 @@ + + + + + + + + @@ -459,6 +467,27 @@ +
  • + + + + + Coding guide + + + + +
  • + + + + + + + + + +
  • diff --git a/docs/essays/template-data-structures/index.html b/docs/essays/template-data-structures/index.html index 7c1c1d4..290ee81 100644 --- a/docs/essays/template-data-structures/index.html +++ b/docs/essays/template-data-structures/index.html @@ -28,6 +28,14 @@ + + + + + + + + @@ -459,6 +467,27 @@ +
  • + + + + + Coding guide + + + + +
  • + + + + + + + + + +
  • @@ -1315,9 +1344,11 @@
    @@ -1410,8 +1441,8 @@

    Your essay's title

    @@ -1462,7 +1493,7 @@

    Your essay's title @@ -1568,7 +1599,7 @@

    2.n+1 Summary @@ -1702,7 +1733,7 @@

    4 Performance @@ -1807,8 +1838,8 @@

    4.1 Generating inputs @@ -1852,7 +1883,7 @@

    4.2 Best, normal and worst run-times @@ -1865,8 +1896,8 @@

    4.2 Best, normal and worst run-times @@ -1912,7 +1943,7 @@

    4.3 Fastest and slowest algorithm diff --git a/docs/essays/template-intro-programming/index.html b/docs/essays/template-intro-programming/index.html index 3a8970b..e69edee 100644 --- a/docs/essays/template-intro-programming/index.html +++ b/docs/essays/template-intro-programming/index.html @@ -28,6 +28,14 @@ + + + + + + + + @@ -459,6 +467,27 @@ +
  • + + + + + Coding guide + + + + +
  • + + + + + + + + + +
  • @@ -1255,9 +1284,11 @@ @@ -1399,7 +1430,7 @@

    Your essay's title diff --git a/docs/example-essays/index.html b/docs/example-essays/index.html index 4797727..7832080 100644 --- a/docs/example-essays/index.html +++ b/docs/example-essays/index.html @@ -32,6 +32,14 @@ + + + + + + + + @@ -541,6 +549,27 @@ +
  • + + + + + Coding guide + + + + +
  • + + + + + + + + + +
  • @@ -871,9 +900,17 @@

    Example essays

    To get an editable version of a template to start writing your own essay, right-click on a download button. From the pop-up menu, choose ‘Save file as…’ (or similar) to save the template in a folder of your choice.

    -

    If you’re using Deepnote, you don’t need to download anything because +

    +
    +

    Colab

    +

    You don’t need to download any template because you can open them directly, +as explained in Getting started (Colab).

    +

    Sum of 1 to n

    This is a short and simple essay, suitable for those on introductory programming courses. The essay shows two ways of calculating 1 + 2 + … + n and compares their run-times. diff --git a/docs/feedback/index.html b/docs/feedback/index.html index 948894d..3e65124 100644 --- a/docs/feedback/index.html +++ b/docs/feedback/index.html @@ -11,7 +11,7 @@ - + @@ -32,6 +32,14 @@ + + + + + + + + @@ -463,6 +471,27 @@ + + +

  • + + + + + Coding guide + + + + +
  • + + + + + + + + @@ -1004,7 +1033,7 @@

    Crediting feedback

    + +
  • -

  • - -
  • - - - Charting run-times - - + + + + + -
  • - -
  • - - - Interpreting run-times - - -
  • - - - - - -
  • - - - Final check - - -
  • - -
  • - - - Further reading - - +
  • + + -
  • - - + + Coding guide + - - + + @@ -1005,132 +899,6 @@ - - - - -
  • - - - Performance analysis - - - - -
  • @@ -1140,15 +908,6 @@ -
  • - -
  • - - - Further reading - - -
  • @@ -1170,9 +929,9 @@

    Writing guide

    -

    This document provides guidance on how to produce your essay.

    -
    -

    Note

    +

    This document provides guidance on how to produce your essay.

    +
    +

    Warning

    Although we wish to accommodate novice programmers in the future, the guide currently has data structures and algorithms students in mind.

    @@ -1182,8 +941,8 @@

    Writing guide

    You may wish to first pitch your essay idea to your peers, to recruit co-authors.

    In the rest of this guide, ‘you’ and ‘your’ are both singular and plural pronouns, to refer simultaneously to a single author or multiple authors.

    -
    -

    Note

    +
    +

    Tip

    You may wish to keep this guide open while going through your copy of our template.

    @@ -1221,13 +980,21 @@

    Text

    The essay should thus have a clear narrative, going from the problem to the conclusion.

    An algorithmic essay contains more text than code, and while code can and should have comments, the text carries most of the explanation. It’s thus important for the text to be clear and error-free.

    -

    Deepnote notebooks can have rich-text cells (headings, paragraph, bullet item, etc.) that, +

    Essays can be written in any style: it’s a personal choice. +For example, you can use ‘we’, ‘I’ or an impersonal form.

    +
    +

    Tip

    +

    Strunk and White’s The Elements of Style is a classic. +The examples are dated but the advice is good.

    +
    +
    +

    Deepnote

    +

    Notebooks can have rich-text cells (headings, paragraph, bullet item, etc.) that, contrary to the Markdown cells, are spell-checked as you write the text and support keyboard shortcuts, like Ctrl + B to put the selected text in bold. Unless you want to keep your essays in Deepnote, we do not recommend using rich-text cells, as their formatting is lost when downloading the notebook to your computer.

    -

    Essays can be written in any style: it’s a personal choice. -For example, you can use ‘we’, ‘I’ or an impersonal form.

    +

    Structure

    An essay starts with a title that states the problem or the algorithmic technique to be used. Next, put your name(s) and the current date, which should be updated whenever you edit the essay.

    @@ -1273,422 +1040,7 @@

    Algorithms

    You should include worst-case complexity analyses of the various solutions you propose, as this helps discard the inefficient ones that may not be worth implementing.

    Code

    -

    Your code should be correct, simple, and as readable as possible. -Unless the aim of your essay is to discuss advanced Python constructs, -try to use only a basic subset of the language. This allows more people, -including those with limited knowledge of Python, to understand your code. -It also makes your code easier to port to other programming languages.

    -

    We recommend the following workflow, which is further explained in the following subsections.

    -
      -
    1. Write the tests for your algorithms.
    2. -
    3. Implement the algorithms and run the tests.
    4. -
    5. Typecheck your code as you run each cell.
    6. -
    7. Format your code, cell by cell.
    8. -
    9. Check the code style as you run each cell.
    10. -
    -

    Writing the tests (step 1) before the code they test (step 2) is a cornerstone of -test-driven development, a widely used practice. Thinking of the tests early -in the process helps you better understand the problem and think of correct solutions.

    -
    -

    Info

    -

    If you followed our ‘getting started’ instructions, the software mentioned -in the next subsections to carry out the above workflow is already installed.

    -
    -

    Testing

    -

    You should write tests for each function, to have some assurance that it is correct. -Tests that check the behaviour of a single function are called unit tests. -The unit tests should cover normal cases and edge cases: extreme input values and -inputs that lead to extreme output values.

    -

    For each input, the smallest possible value, e.g. zero or the empty list, is an edge case, -and so is the largest possible value, if there is one for that input. -If a function is doing a search for an item in a list, then edge cases would be the item being -at the start, at the end, or not occurring at all. -If the output is a list, then inputs that produce the empty list are edge cases too. -In summary, try to think of the ‘trickiest’ inputs the algorithm has to cope with.

    -

    We provide a small library to support algorithmic essays: algoesup. -It allows you to easily write and run unit tests. Here’s an example. -(The # fmt: off and # fmt: on lines will be explained later.) -

    from algoesup import test
    -
    -# function to be tested
    -def absolute_difference(x: int, y: int) -> int:
    -    """Return the absolute value of the difference between x and y."""
    -    return x - y  # deliberately wrong, should be abs(x - y)
    -
    -# fmt: off
    -# unit tests in tabular form, one test per row
    -unit_tests = [
    -    # test case,  x,    y,    expected result
    -    ("x == y",    1,    1,    0),
    -    ("x > y",     10,   -1,   11),
    -    ("x < y",     -1,   10,   11),
    -]
    -# fmt: on
    -
    -# run the function on all test inputs and compare the actual and expected outputs
    -test(absolute_difference, unit_tests)
    -

    -
    -

    Output

    -

    Testing absolute_difference…
    -x < y FAILED: -11 instead of 11
    -Tests finished: 2 passed, 1 failed.

    -
    -

    A unit test consists of the input values to pass to your function and the output value you’re expecting. -The library requires a short descriptive string for each unit test, so that it can indicate which tests failed. -The library expects unit tests to be in tabular format: one row per test, and -one column for the description, one column for each input, and one column for the expected output. -In the example above, the test table is a list of tuples, but it could as well be -a list of lists, a tuple of lists, or a tuple of tuples.

    -

    You should reuse the test table for all solutions, because they’re about the same problem. -Here’s a correct function that passes all test cases. -

    def absolute_difference_without_abs(x: int, y: int) -> int:
    -    """Return the absolute value of the difference between x and y.
    -
    -    This solution doesn't use the built-in abs() function.
    -    """
    -    if x > y:
    -        return x - y
    -    else:
    -        return y - x
    -
    -test(absolute_difference_without_abs, unit_tests) # same test table
    -

    -
    -

    Output

    -

    Testing absolute_difference_without_abs…
    -Tests finished: 3 passed, 0 failed.

    -
    -

    Type checking

    -

    As the above examples show, your code should contain type hints -like x: int and ... -> int to indicate the type of the input and of the output. -They make your code easier to understand, and help type checkers detect any type mismatches, -like passing a string instead of an integer.

    -

    The algoesup library also provides an extension for Jupyter notebooks, which you must load first. -

    %load_ext algoesup.magics
    -
    -(Magics are special commands that can change the behaviour of running a code cell.) -You can now turn on type checking as follows. -
    %pytype on
    -

    -
    -

    Output

    -

    pytype was activated

    -
    -

    Words that start with % are special commands (‘magics’) for IPython, the Python interpreter used by Jupyter notebooks. -The %pytype command, provided by our library, activates Google’s pytype type checker.

    -

    Once the type checker is activated, it checks each cell immediately after it’s executed. -In this way you can detect and fix errors as you write and run each code cell. -Here’s an example of what happens. -

    def double(x: int) -> int:
    -    """Return twice the value of x."""
    -    return x * 2
    -
    -double([4])
    -

    -
    -

    Output

    -

    [4, 4]

    -

    pytype found issues:

    -
      -
    • 5: Function double was called with the wrong arguments [wrong-arg-types]
    • -
    -
    -

    The function is executed and produces an output because lists can also be ‘multiplied’ with an integer, -but the type checker detects that line 5 should have passed integers, not lists of integers, to the function. -Clicking on the error name in square brackets leads you to pytype’s website, with more info.

    -

    When a type checker only processes one cell at a time, it is missing the wider context, -like the previously defined functions. Therefore, pytype won’t spot all type errors. -However, some checking is better than no checking.

    -

    The type checker adds some seconds to the overall time to run each code cell. -You may thus wish to initially turn off the type checking, with %pytype off, -and only turn it on after all code is written and tested. -You will have to run all cells of your notebook for the type checking to take place.

    -

    For a list of all the options for the %pytype command, -see the library reference.

    -

    Formatting

    -
    -

    Note

    -

    This subsection only applies to Deepnote.

    -
    -

    Once you have written, tested and type checked all your code, you should format it so that -it follows the Python community’s code style. -You will need to format each cell, as explained here.

    -

    If there’s a block of code that you don’t want the formatter to change, -write # fmt: off on its own line before the block and write # fmt: on after the block, -to temporarily switch off formatting for that block. -This feature is especially useful for keeping the format of unit test tables, -as shown in an earlier example.

    -

    The Deepnote formatter automatically enforces simple formatting conventions, like -4 spaces for indentation and 2 empty lines between functions, -so you will see fewer warnings in the next stage.

    -

    Linting

    -

    You should lint your code, which means to check it for style violations.

    -

    Code style

    -

    Our library supports ruff, the fastest Python linter. -To turn it on, write the following after loading the algoesup.magics extension. -

    %ruff on
    -

    -
    -

    Output

    -

    ruff was activated

    -
    -

    From now on, each cell is automatically linted after it’s executed. Here’s an example: -

    l = [1, 2, 3]
    -if (not 5 in l) == True:
    -    print("5 isn't in the list")
    -

    -
    -

    Output

    -

    5 isn’t in the list

    -

    ruff found issues:

    -
      -
    • 1: [E741] Ambiguous variable name: l
    • -
    • 2: [PLR2004] Magic value used in comparison, consider replacing 5 with a constant variable
    • -
    • 2: [E713] Test for membership should be not in. Suggested fix: Convert to not in
    • -
    • 2: [E712] Comparison to True should be cond is True or if cond:. Suggested fix: Replace with cond is True
    • -
    -
    -

    Every message indicates the line of the problem.

    - -

    As this code cell shows, ruff sometimes suggests how to fix the reported error, -but you must consider whether the suggestion is appropriate.

    -

    If you don’t understand an error message, like the first one, click on -the error code in brackets, to get more information from ruff’s website.

    -

    Like for type checking, linting one cell at a time means that the linter is -unaware of the wider context of your code. For example, in notebooks, -variables may be defined in one cell but used in a later cell. As the linter -checks each cell separately, it would report an undefined variable in the later cell. -We have disabled checks for undefined variables and other checks that would lead to -irrelevant error messages in notebooks, which means that genuine undefined variables -won’t be flagged. But again, some linting is better than none.

    -

    If you get errors that you think are irrelevant, -you can disable them with the --ignore option: -see the library reference.

    -

    Language subset

    -

    Our library also supports the allowed linter, created by ourselves. -It checks whether your code only uses a certain subset of the Python language. -This gives you some reassurance that your code will be understood by a wide audience.

    -

    By default, allowed checks against the Python subset used in our -algorithms and data structures course. -So, if you’re an M269 student, to check that your essay is easily understood -by your peers in terms of Python constructs, just add the following after loading the extension: -

    %allowed on
    -

    -
    -

    Output

    -

    allowed was activated

    -
    -

    Henceforth, after a cell is executed, the allowed linter will list any constructs, -modules or built-in types we haven’t taught, like this: -

    from math import pi, sin
    -
    -print(f"π is approximately {pi:.5f}.")
    -

    -
    -

    Output

    -

    π is approximately 3.14159.

    -

    allowed found issues:

    -
      -
    • 1: sin
    • -
    • 3: f-string
    • -
    -
    -

    We haven’t taught the math.sin() function nor f-strings, and allowed reports these.

    -

    Any line that ends with the comment # allowed is ignored. This is useful when -you don’t want the linter to flag a construct that you explain in your essay. -For example, adding the comment after print(...) would not report the f-string. -Note that the comment makes the tool skip the whole line: -if it has several constructs that weren’t taught, none of them is reported.

    -

    The allowed linter also includes the configuration for TM112, our introductory Computing course, -in case you want to use even fewer constructs in your essay. -To use that configuration, write %allowed on --config tm112.json. -For a list of all the options for the %allowed command, -see the library reference.

    -

    You can configure the linter with a JSON file that lists the allowed constructs. -In Deepnote, rename -the allowed.json JSON configuration in the Files section of your project, -and adapt it to your course. -See the allowed website for instructions.

    -

    Performance analysis

    -

    Complexity analysis gives an indication of how the run-times will grow as the inputs grow, -but it can’t predict the exact run-times nor which algorithm is in practice fastest.

    -

    Our library helps measure and plot the run-times of one function on different kinds of input, -or of different functions on the same inputs.

    -

    For example, let’s suppose our essay is about sorting algorithms and we have implemented selection sort. -

    def selection_sort(values: list[int]) -> list[int]:
    -    """Return a copy of the values, in ascending order."""
    -    result = values[:]
    -    for current in range(len(result) - 1):
    -        # select the smallest element in result[current:] ...
    -        smallest = current
    -        for index in range(current + 1, len(result)):
    -            if result[index] < result[smallest]:
    -                smallest = index
    -        # ... and swap it with the current element
    -        result[current], result[smallest] = result[smallest], result[current]
    -    return result
    -

    -

    Generating inputs

    -

    To measure the run-times of sorting algorithms on increasingly large lists, -we must implement functions that generate such lists. For example, we can write -a function that generates lists that are already in ascending order, -which is a best case for many sorting algorithms, and -a function that generates lists that are in descending order, -which is a worst case for some sorting algorithms.

    -

    The library expects such input-generating functions to take a non-negative integer n, -and to produce a tuple of input values, with total size n. Why a tuple? -Although our sorting algorithm takes a single input (a list of integers), -many algorithms take more than one input. Thus the input-generating functions must -generate a tuple of inputs, in the same order as expected by the algorithm. -

    def ascending(n: int) -> tuple[list[int]]:
    -    """Return a list of n integers in ascending order."""
    -    return (list(range(1, n + 1)),)  # trailing comma to make it a tuple
    -
    -def descending(n: int) -> tuple[list[int]]:
    -    """Return a list of n integers in descending order."""
    -    return (list(range(n, 0, -1)),)
    -
    -We should of course test these functions, to make sure they produce the expected lists, -but we will skip that in this explanation because we’re focusing on how to measure run-times.

    -

    Comparing cases

    -

    To measure the run-times of a function f on best, average and worst case inputs, use -library function time_cases(f, [case1, case2, ...], s, d). -The second argument can be a list (or tuple) of up to 6 input-generating functions. -The time_cases function works as follows.

    -
      -
    1. Call case1(s) to generate inputs of size s for f.
    2. -
    3. Run function f on the generated inputs and measure its run-time.
    4. -
    5. Do the two previous steps with each of the functions case2, ....
    6. -
    7. Set s to double its value and go back to step 1.
    8. -
    -

    The whole process stops when s has been doubled d times. -If d is zero, the run-times are only measured for size s.

    -

    Here’s how we could measure the run-times for selection sort on ascending and descending lists. -

    from algoesup import time_cases
    -
    -time_cases(selection_sort, [ascending, descending], start=100, double=4)
    -

    -
    -

    Output

    -
    Run-times for selection_sort
    -
    -Input size       ascending      descending
    -       100           168.2           173.2 µs
    -       200           643.2           660.6 µs
    -       400          2716.7          2817.9 µs
    -       800         11072.4         11407.3 µs
    -      1600         44285.3         45512.7 µs
    -
    -
    -

    Running selection sort on lists from 100 to 1600 integers takes about 170 microseconds to 45 milliseconds. -To measure precisely such small time spans, function f (here, selection_sort) is called -multiple times on the same input, within a loop, and the total time is divided by -the number of iterations, to obtain a better estimate of the time taken by a single call to f. -The whole process is repeated 3 times, because the run-times will vary due to other processes running on the computer. -The lowest of the 3 run-times is reported.

    -

    Because function f is called multiple times, it is very important that f does not modify its inputs. -For example, if selection_sort sorted the list in-place, instead of returning a new list, -then the first call would put the numbers in ascending order, and the subsequent calls -would just try to sort an already sorted list, swapping no numbers. -We would obtain almost exact same times for ascending and descending input lists, -instead of always larger times for descending lists, as shown above.

    -

    When executing a code like the previous one, be patient while waiting for the results. -Even though each call may just take a few milliseconds or less, the code cell will take several seconds or -even minutes to execute, because the function is called many times to make the measurements more robust.

    -

    Comparing functions

    -

    Our library also allows you to compare different algorithms for the same input case. -For that, use time_functions([f1, f2, ...], case, s, d), which does the following: -1. Call case(s) to generate inputs of total size s. -2. Call each function f1, f2, etc. on the generated inputs and measure their run-times. -3. Double the value of s and go to step 1, unless s has been doubled d times.

    -

    The run-times are measured as for time_cases: take the best of 3 run-times, each obtained by -calling the function within a loop and dividing the total time by the number of iterations.

    -

    Here’s a comparison of the built-in sorted function against selection sort, on descending lists. -

    from algoesup import time_functions
    -
    -time_functions([selection_sort, sorted], descending, start=100, double=4)
    -

    -
    -

    Output

    -
    Inputs generated by descending
    -
    -Input size  selection_sort          sorted
    -       100           172.8             0.5 µs
    -       200           660.7             0.8 µs
    -       400          2795.7             1.6 µs
    -       800         11534.0             3.1 µs
    -      1600         45470.0             5.9 µs
    -
    -
    -

    As expected, the built-in sorting implementation is much, much faster.

    -

    Charting run-times

    -

    If you add argument chart=True to time_cases or time_functions, then -you will see a line chart of the run-times, in addition to the exact run-times. -If you only want to see the chart, then add arguments text=False, chart=True.

    -
    time_cases(sorted, [ascending, descending], 100, 4, text=False, chart=True)
    -
    -
    -

    Output

    -

    chart only

    -
    -
    time_functions([sorted, selection_sort], ascending, 100, 4, chart=True)
    -
    -
    -

    Output

    -

    Inputs generated by ascending
    -
    -Input size          sorted  selection_sort
    -       100           482.1        168025.5 ns
    -       200           808.3        646093.8 ns
    -       400          1496.9       2720369.2 ns
    -       800          2850.4      11090135.4 ns
    -      1600          5553.0      44372758.4 ns
    -
    -times and chart

    -
    -

    The 1e7 above the y-axis means that the run-times must be multiplied by 10⁷, i.e. 10 million.

    -

    Note that when calling time_functions([selection_sort, sorted], ...) the run-times were reported in microseconds, -but when calling time_functions([sorted, selection_sort], ...) they were in nanoseconds. -The reason is that the library chooses the time unit based on the first run-time measured. -If there’s a big time difference between the fastest and slowest cases or algorithms, -you may wish for the first function in the list to be the slowest one, to report it with -small values in a ‘large’ time unit, instead of very large values in a ‘small’ time unit. -So, in time_functions([f1, f2, ...], case, ...) the slowest function should be f1, -and in time_cases(f, [case1, case2, ...], ...) the worst case should be case1.

    -

    Interpreting run-times

    -

    If, as the input size doubles, the run-times…

    - -

    Looking at the run-times reported in the previous subsections, we can see that -sorted is linear because the run-times about double when the input size doubles, -whereas selection sort is quadratic because the run-times increase about 4-fold when the input size doubles.

    -

    Remember that run-times vary every time you execute a cell because the computer -is executing other processes. This may lead to the odd behaviour here and there. -For example, we have noted that sorted is occasionally faster for descending lists, -which is counter-intuitive because it does have to reverse them.

    -

    If you can’t see any trend in the run-times, or they aren’t what you expect, -one possible cause is that the input sizes are too small. -Increase start and run again the code cell.

    -

    If after increasing the start size several times you still don’t get the run-times -you expect from your complexity analysis, then there might be other explanations:

    - -

    For an example of the latter, see the Jewels and Stones essay.

    +

    See our coding guide.

    Final check

    Whether it’s your essay’s first draft or final version, before you share it with others, you should restart the kernel and run all cells, so that you have a ‘clean’ version. @@ -1696,23 +1048,16 @@

    Final check

    and fix any typos or missing explanations you find.

    Look at the table of contents of your notebook and check that your section headings are at the right level.

    -
    -

    Info

    -

    In Deepnote, the table of contents is on the left sidebar; -in Colab, you must click the bullet list icon in the left sidebar.

    +
    +

    Deepnote

    +

    The table of contents is on the left sidebar.

    +
    +
    +

    Colab

    +

    To see the table of contents, click the bullet list icon in the left sidebar.

    Finally, let others comment on your essay and help you produce a better version. See our feedback guide for details.

    -

    Further reading

    -

    If you’re interested and have the time, here are further details on some of the above.

    -
      -
    • Strunk and White’s The Elements of Style is a classic. The examples are dated but the advice is good.
    • -
    • The websites of allowed, pytype and ruff.
    • -
    • A summary of Python’s type hints provided by the mypy project (another type checker).
    • -
    • The Python code style and docstring conventions.
    • -
    • The formatting style enforced by black, which we suspect is - the formatter used by Deepnote. Deepnote ignores the # fmt: skip directive to not format a single line.
    • -
    @@ -1759,13 +1104,13 @@

    Further reading

    - +