Brainteaser and Python sets

The ABC Radio web site has a regular Friday brain teaser:

If you want to solve the problem yourself, read no further because a spoiler follows!

Investigation of the solution to this problem led me to looking at the use of Python sets, which are unordered collections of unique, hashable elements. For the current problem the important properties are:

  • The members are unique, so if you try to add a member that already exists, it won’t change the set.
  • The set is hashable, which means it can be searched very quickly.

For more detailed information on the use of sets see: Real Python – Sets in Python

The solution to the problem is that for each word in the list, if you substitute the first letter with the preceding or following alphabetical letter, it is still an English word. This raised the question: how many English words are there that have this property? To investigate that, first we need a list of all English words, which I found at: Words.alpha.txt

The following Python code creates an Excel user defined function (UDF) that:

  • Opens the selected text file
  • Copies each line to a set called words
  • For each word in words, it removes the first letter and constructs new words starting with the preceding and following letters.
  • If both of the new words are found in the words set, it adds 1 to the total number of qualifying words and all three words to a set called qualifying.
  • Finally it returns the total number of words in the supplied dictionary, the number of qualifying words, and the list of all qualifying words and their neighbours.
@xl_func
@xl_return('numpy_array')
def wordcount(dictfile):
    __location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))
    with open(os.path.join(__location__, dictfile), encoding='utf-8') as dict :
        words = set()
        for line in dict:
            word = line.strip().lower()
            if word.isalpha():  # Only consider alphabetic words
                words.add(word)

    qualifying = set()
    numwords = 0
    # Find all words that have both a previous and next letter neighbor in the dictionary
    for word in words:
        if len(word) < 2:
            continue
        suffix = word[1:]
        first_letter = ord(word[0])
        previous_letter = first_letter - 1
        if previous_letter >= ord('a'):
            pneighbour = chr(previous_letter) + suffix
            if pneighbour in words:
                next_letter = first_letter + 1
                if next_letter <= ord('z'):
                    nneighbour = chr(next_letter) + suffix
                    if nneighbour in words:
                        numwords += 1
                        qualifying.add(pneighbour)
                        qualifying.add(nneighbour)
                        qualifying.add(word)
    qualifying = sorted(qualifying, key=lambda x: x[1:])  # Sort by the second letter of the word
    rtn = np.zeros((len(qualifying)+2, 1), dtype=object)
    rtn[0, 0] = len(words)
    rtn[1, 0] = numwords
    rtn[2:, 0] = np.array(list(qualifying), dtype=object).reshape(-1)
    return rtn

Output from the UDF.

The UDF output is in column R. I have also copied the results as text and sorted by word length, descending, then alphabetically with the first character removed, then by first character.

Before creating the Python code, I did the same job directly in Excel, without any coding:

The Excel based procedure was:

  • Import the dictionary file as text.
  • Count the letters on each line and sort by size.
  • For words with two or more letters, separate the first letter and the rest of the word in two adjacent columns.
  • Sort by the tail word, then by the first letter.
  • Enter a formula to check for each group of three words if the tail words were the same and the first letters were in alphabetical order (see the formula in Cell F28 in the screenshot).
  • For qualifying groups, enter a 1 in Column F for the central word and in Column G for all three words.
  • Sum the number of qualifying groups, which came to 1596, the same as the Python UDF.
Posted in Excel, Link to Python, PyXLL, UDFs | Tagged , , , , | Leave a comment

py_RC Design with 3D Cache

Following my recent post on passing 3D arrays from Python to Excel I have now added a py_UmomC function to the py_RC Design spreadsheet. The new spreadsheet and Python code, along with related code, can be downloaded from:

py_RC Design.zip

Also see py_RC Design 2 for more details of the other functions in the spreadsheet, and Python and pyxll for information about the required pyxll add-in, together with a discount voucher for new users.

The new py_UmomC function returns all the results from py_Umom as a cache object, which displays the cache name in a single cell. If the results are for a single axial load and/or cross section the cache is 2D, but if the input axial load was an array the cache is a 3D array, with each “sheet” having the results for each axial load value.

The chosen results can be displayed with the py_array3D function. In the example below results are displayed for Column 11, Row 3 (the ultimate shear capacity), and because the Sheet is entered as zero results are returned for all input axial loads:

Changing the Column/Row values to 2 and 4 returns the ultimate bending capacity:

The py_Umom function can also return results for an input array of section depths, optionally with associated reinforcement areas. The axial load may be varying or constant:

As before, different results are returned using the py_Array3D function calling the same cache object with different column and/or row specified:

If the output row is entered as 0 the results are returned as a 2D array, with all the results for the specified column and each axial load in the results columns:

Specifying a single column and sheet returns a column array for the specified axial load, and/or cross section:

and specifying Sheet, Column, and Row returns a single value:

Posted in Arrays, Beam Bending, Concrete, Excel, Link to Python, Newton, NumPy and SciPy, PyXLL, UDFs | Tagged , , , , , , , , , , | 2 Comments

Josh Turner

We haven’t had any Bach posts for a bit, so here is new (to me) guitarist Josh Turner playing a cover of Bert Jansch’s version of Blackwaterside:

and another example of his varied work, a cover of Nick Drake’s Three Hours:

and another Nick Drake cover, One of These Things First:

Posted in Bach | Tagged , , , , | Leave a comment

VBA polynomial functions update

Following several comments at an old post I have made updates to the Polynomial spreadsheet. The new files can be downloaded from:

Polynomial.zip

The changes are:

  • SolvePoly was amended to accept numbers or signed cell addresses in the input.
  • QuarticR, CubicR, QuadraticR and SolvePolyR functions were added, returning only the real roots.
  • SolvePoly was modified to return all real roots before the complex roots, and sort all real roots in ascending order in all cases.
  • The CubiCC was found to have been corrupted at some stage, and was returning incorrect results in some cases. It has been corrected and verified against the SolvePoly function and by checking the returned error values.
  • SolvePoly was modified to allow the first input argument to be either a single row or column range, with any remaining coefficients entered as single cells or values.
  • The Python polynomial functions have been moved to the pyNumpy module.

New functions returning real roots only:

Updated SolvePoly and SolvePolyR:

SolvePoly with an array as the first argument:

Also see Complex Numbers and Solving Quartic Polynomial Equations and Solving higher order polynomials.

For links to the latest Python polynomial functions see Calling Numpy polynomial functions from Excel.

Posted in Excel, Link to Python, Maths, Newton, NumPy and SciPy, PyXLL, UDFs, VBA | Tagged , , , , , , , | Leave a comment

Passing 3D arrays to Excel with Python and pyxll

The previous post looked at options for displaying 1D or 2D arrays in Excel. This post will look at passing 3D arrays from Python to Excel as a cache object, using pyxll, and how to extract selected data from the cache. It will use the py_Umom function, as in the last post, with the addition of functions to handle the cache object. The new code is still being finalised, but will be made available for download when complete.

The py_Umom function calculates the ultimate strength of a reinforced concrete section subject to specified applied loads. Results of the analysis are available in 12 different column arrays, one of which must be chosen when the function is entered. The screenshot below shows the first 3 of the 12 results arrays, each of which must be entered as separate function:

The new py_UmomC function combines all 12 columns into a 2D array. In addition it is possible to calculate results for any number of different applied axial loads, in which case all the results are combined into a 3D array. In either case, the results are returned as a cache object, which displays as text in a single cell. Chosen results from the cache can then be displayed using the py_array3D function, as illustrated below.

Input for the py_UmomC function is the same as for py_Umom, except that no output index values are required because the function returns all available results. In the example below the py_UmomC function is entered in cell L2, and the input includes a range of 21 different axial loads:

Selected results can then be displayed with the py_array3D functions, which has options for displaying a selected “sheet”, and/or optionally selected columns or rows. If column and row are not selected the function returns all results for the specified axial load:

If a column is specified, and the sheet is set to zero, the function returns results for the specified column over the full range of axial loads, returned as a 2D array:

If a row is then specified the results for that value over the input axial load range are returned as a column array. In the example below the results are total design bending capacity:

Any other results can then be extracted from the results cache, for example total design shear capacity:

Posted in Arrays, Excel, Link to Python, Newton, PyXLL, UDFs | Tagged , , , , , | 1 Comment