Skip to content

Commit 46a942a

Browse files
committed
Added Python Example and Explanation
1 parent 8c62a88 commit 46a942a

File tree

5 files changed

+247
-25
lines changed

5 files changed

+247
-25
lines changed

Python/README.md

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# Thief
2+
In this code challenge we write a program that displays every possible combination of a 4 digit PIN.
3+
4+
## Problem
5+
A thief has managed to find out the four digits for an online PIN code, but doesn't know the correct sequence needed to hack into the account.<br>
6+
7+
Design and write a program that displays all the possible combinations for any four numerical digits entered by the user. The program should avoid displaying the same combination more than once.
8+
9+
## Solution
10+
In the solution (found in `solution.py`) we are going to try and produce every possible combination including duplicates.
11+
I find this solution will be easier for a beginner to follow as it focuses on looping, and less on recursion and generators.
12+
13+
```python
14+
# Function gets every pattern made from a string's characters.
15+
def permutations(string, idx=0):
16+
# We check if the length-1 is the same as the index.
17+
if idx == len(string) - 1:
18+
# This indicates a cycle has been completed and a new pattern has been created.
19+
# We output the new pattern to the user.
20+
print("".join(string))
21+
22+
# Here we simply loop through every character of the string
23+
# NOTE: idx does not change from 0 in this scope only in the recursive scopes
24+
for j in range(idx, len(string)):
25+
letters = list(string)
26+
# Python magic, we are swapping the elements at letters[idx] and letters[j]
27+
letters[idx], letters[j] = letters[j], letters[idx]
28+
# We make a recursive call and increase idx
29+
# Remember once idx is the same as length-1 we will exit back into the original scope.
30+
# Once in the original scope, idx is still 0, and j will increment.
31+
permutations(letters, idx + 1)
32+
```
33+
To see why this solution works, it's best to unravel the loops and function calls.
34+
This allows us to see exactly what's happening without all the hidden parts.
35+
Since unravelling everything can make our code very long, we will swap out a 4 digit PIN with a set of two letters.
36+
```python
37+
# Original scope of the function
38+
permutations(string, idx=0):
39+
string = "ab"
40+
idx = 0
41+
if idx == len(string) - 1: # This is false
42+
print("".join(string))
43+
44+
# First cycle of for loop
45+
j = 0
46+
letters = list(string)
47+
letters[idx], letters[j] = letters[j], letters[idx] # swap "a" with "a" (does nothing)
48+
49+
# Recursive function call (increment idx by 1)
50+
permutations(letters, idx + 1):
51+
idx = 1
52+
if idx == len(letters) - 1: # This is true
53+
print("".join(letters)) # print "ab"
54+
j = 1
55+
letters[idx], letters[j] = letters[j], letters[idx] # swap "b" with "b" (does nothing)
56+
57+
# Recursive function call (increment idx by 1)
58+
permutations(letters, idx + 1):
59+
idx = 2
60+
if idx == len(string) - 1: # This is false
61+
print("".join(string))
62+
j = 2
63+
# Loop does not occur as j and idx are the same.
64+
65+
# Loop finishes because j is equal to len(string).
66+
67+
# Next cycle of the for loop
68+
j = 1
69+
letters[idx], letters[j] = letters[j], letters[idx] # swap "a" with "b" (letters is now "ba")
70+
71+
# Recursive function call (increment idx by 1)
72+
permutations(letters, idx + 1):
73+
idx = 1
74+
if idx == len(letters) - 1: # This is true
75+
print("".join(letters)) # print "ab"
76+
j = 1
77+
letters[idx], letters[j] = letters[j], letters[idx] # swap "b" with "b" (does nothing)
78+
79+
# Recursive function call (increment idx by 1)
80+
permutations(letters, idx + 1):
81+
idx = 2
82+
if idx == len(string) - 1: # This is false
83+
print("".join(string))
84+
j = 2
85+
# Loop does not occur as j and idx are the same.
86+
87+
# Loop finishes because j is equal to len(string).
88+
89+
# Loop finishes
90+
# Function exits
91+
# We have printed both "ab" and "ba", all possible combinations.
92+
```
93+
94+
## Extension
95+
In the extension, our goal is to ensure none of the combinations are duplicates.
96+
97+
### Extension Solution
98+
In our extension solution, we make use of Python's generators, and recursion to make a generator that calls itself and yields back along calls until the user of the generator receives the calls.
99+
The solution below can be found in `solution_advanced.py`.
100+
```python
101+
# Our new function is a generator.
102+
# This means it yields values every cycle of a loop.
103+
def permutations(string):
104+
if len(string) <= 1:
105+
yield string
106+
else:
107+
# The recursive function calls yield values backwards until they reach the
108+
# original function scope, in which they are yielded back to the user.
109+
for i in range(len(string)):
110+
for p in permutations(string[:i] + string[i + 1:]):
111+
yield string[i] + p
112+
```
113+
We would then draw yielded values from the generator using our own loop.
114+
```python
115+
for pattern in permutations("ABC"):
116+
print(pattern)
117+
```
118+
This will print every possible combination from the string "ABC".
119+
However, we must still filter out the duplicates.
120+
To do this, we would create another list and append all values from patterns that is not already in our list.
121+
This means when we first come across a value we append it, if we find a value that is the same we will not append it as it is already in the list.
122+
```python
123+
# Here we loop through the generator's yields to get every pattern possible.
124+
patterns = [pattern for pattern in permutations(PIN)]
125+
126+
# Here we filter out repeated values
127+
unique_patterns = []
128+
for pattern in patterns:
129+
# If a pattern is already in unique_patterns then it is a duplicate.
130+
# We do not print or append the duplicate pattern.
131+
if pattern not in unique_patterns:
132+
unique_patterns.append(pattern)
133+
print(pattern)
134+
```
135+
136+
### Extension Solution 2 (Real world solution)
137+
Python is a language with "batteries included" a phrase meaning the language has lots of built-in ways of handling tasks we may want to do.
138+
In some programming languages, writing our own `permutation` function is required as the language does not have a built-in way of getting the permutations of a string or list.
139+
However, in Python, there is a library called `itertools` already available to us.
140+
`itertools` has a function called `permutations` that allows us to get every possible combination of a string or list very quickly.
141+
142+
An example of `itertools.permutations` can be found in `solution_advanced2.py`.
143+
```python
144+
# itertools.permutations(PIN) is a generator that yields every pattern.
145+
patterns = [pattern for pattern in itertools.permutations(PIN)]
146+
```
147+
The `itertools` library is part of Python's standard library.
148+
This means, whenever you are using Python, you will always have access to `itertools` and `itertools` is written in the C programming language.
149+
This makes `itertools` much faster than our Python versions.
150+

Python/solution.py

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,22 @@
1-
import itertools
2-
3-
word = "cowboy"
4-
5-
x = itertools.permutations(word)
6-
for i in x:
7-
print(i)
8-
9-
a_string = 'cowboy'
10-
11-
12-
def get_permutation(some_string, idx=0):
13-
if idx == len(some_string) - 1:
14-
print("".join(some_string))
15-
for j in range(idx, len(some_string)):
16-
words_list = [c for c in some_string]
17-
words_list[idx], words_list[j] = words_list[j], words_list[idx]
18-
19-
get_permutation(words_list, idx + 1)
20-
21-
22-
permutations = get_permutation(a_string)
23-
print(permutations)
1+
# Function gets every pattern made from a string's characters.
2+
def permutations(string, idx=0):
3+
# We check if the length-1 is the same as the index.
4+
if idx == len(string) - 1:
5+
# This indicates a cycle has been completed and a new pattern has been created.
6+
# We output the new pattern to the user.
7+
print("".join(string))
8+
9+
# Here we simply loop through every character of the string
10+
# NOTE: idx does not change from 0 in this scope only in the recursive scopes
11+
for j in range(idx, len(string)):
12+
letters = list(string)
13+
# Python magic, we are swapping the elements at letters[idx] and letters[j]
14+
letters[idx], letters[j] = letters[j], letters[idx]
15+
# We make a recursive call and increase idx
16+
# Remember once idx is the same as length-1 we will exit back into the original scope.
17+
# Once in the original scope, idx is still 0, and j will increment.
18+
permutations(letters, idx + 1)
19+
20+
21+
PIN = input("Please enter the PIN: ")
22+
permutations(PIN)

Python/solution_advanced.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Our new function is a generator.
2+
# This means it yields values every cycle of a loop.
3+
def permutations(string):
4+
if len(string) <= 1:
5+
yield string
6+
else:
7+
# The recursive function calls yield values backwards until they reach the
8+
# original function scope, in which they are yielded back to the user.
9+
for i in range(len(string)):
10+
for p in permutations(string[:i] + string[i + 1:]):
11+
yield string[i] + p
12+
13+
14+
PIN = input("Please enter your PIN: ")
15+
# Here we loop through the generator's yields to get every pattern possible.
16+
patterns = [pattern for pattern in permutations(PIN)]
17+
18+
# Here we filter out repeated values
19+
unique_patterns = []
20+
for pattern in patterns:
21+
# If a pattern is already in unique_patterns then it is a duplicate.
22+
# We do not print or append the duplicate pattern.
23+
if pattern not in unique_patterns:
24+
unique_patterns.append(pattern)
25+
print(pattern)

Python/solution_advanced2.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Python has built-in tools for what we want to do
2+
import itertools
3+
4+
PIN = input("Please enter the PIN: ")
5+
6+
# itertools.permutations(PIN) is a generator that yields every pattern.
7+
patterns = [pattern for pattern in itertools.permutations(PIN)]
8+
9+
# Here we filter out the repeated patterns.
10+
unique_patterns = []
11+
for pattern in patterns:
12+
# if a pattern is already in unique_patterns then it is a duplicate
13+
if pattern not in unique_patterns:
14+
unique_patterns.append(pattern)
15+
print(pattern)
16+
17+

README.md

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,37 @@ Design and write a program that displays all the possible combinations for any f
1818
## Solution
1919
*NOTE: The solution explained here is a generalised solution. If you want a language-specific solution read the explanation in the language folder of your choice.*<br>
2020

21-
In this solution, we will produce every combination possible and then filter out the duplicant combinations. This isn't necessarily the most efficient method but will be the easiest to understand. First, we will write a function that takes a string and an index, and produces a list of strings where each letter is replaced by the letter at the index provided. We will then write another function that loops through every index of a string, applies the function previously stated, and stores the combinations. After all that we will filter the list of combinations for duplicants, then finally display the list of combinations to the user.<br>
21+
The solution to this problem is to get the `permutations` of the list. `permutations` are a mathematical construct in which we get every possible combination of items within a `set`.
22+
An example of this is shown below.
23+
```
24+
The permutation of set A={a,b} is 2, such as {a,b}, {b,a}.
25+
```
26+
In our first solution, we will try to brute force the PIN combinations.
27+
We can do this by looping through every single possible combination of numbers made with the numbers of the PIN,
28+
and then filtering out all duplicate letters.
29+
```
30+
pin = INPUT
31+
LOOP FOR EVERY LETTER IN pin
32+
LOOP FOR EVERY LETTER IN pin
33+
LOOP FOR EVERY LETTER IN pin
34+
LOOP FOR EVERY LETTER IN pin
35+
IF NO LETTERS ARE THE SAME
36+
OUTPUT LETTERS COMBINED
37+
```
38+
We can also write this solution in Python to see how we would handle this in actual code.
39+
```python
40+
PIN = input()
41+
for h in PIN:
42+
for j in PIN:
43+
for k in PIN:
44+
for l in PIN:
45+
if h != j and h != k and h != l and j != k and j != l and k != l:
46+
print(h + j + k + l)
47+
```
48+
This method has the most disadvantages.
49+
Firstly, we are looping through every possible combination that can be made with the numbers of the PIN.
50+
This is extremely inefficient. Secondly, our solution only works if every number of the PIN is unique.
51+
So, PINs such as 1292, 0011, 2474, would not work as they have duplicates already.
52+
53+
2254

23-
First let's write a function that produces a list of string where each letter is replaced by the letter at the index provided.

0 commit comments

Comments
 (0)