add hw-3 hw-4 hw-5
This commit is contained in:
BIN
content/en/posts/csci-1100/hw-3/HW3.zip
Normal file
BIN
content/en/posts/csci-1100/hw-3/HW3.zip
Normal file
Binary file not shown.
435
content/en/posts/csci-1100/hw-3/index.md
Normal file
435
content/en/posts/csci-1100/hw-3/index.md
Normal file
@@ -0,0 +1,435 @@
|
||||
---
|
||||
title: CSCI 1100 - Homework 3 - Loops, Tuples, Lists, and Ifs
|
||||
subtitle:
|
||||
date: 2024-03-13T14:28:32-04:00
|
||||
slug: csci-1100-hw-3
|
||||
draft: false
|
||||
author:
|
||||
name: James
|
||||
link: https://www.jamesflare.com
|
||||
email:
|
||||
avatar: /site-logo.avif
|
||||
description: This homework assignment focuses on working with lists, loops, tuples, and if statements in Python. It includes three parts - analyzing text complexity, simulating Pikachu's movement, and modeling population changes of bears, berries, and tourists.
|
||||
keywords: ["Python","loops","tuples","lists","if statements"]
|
||||
license:
|
||||
comment: true
|
||||
weight: 0
|
||||
tags:
|
||||
- CSCI 1100
|
||||
- Homework
|
||||
- RPI
|
||||
- Python
|
||||
- Programming
|
||||
categories:
|
||||
- Programming
|
||||
collections:
|
||||
- CSCI 1100
|
||||
hiddenFromHomePage: false
|
||||
hiddenFromSearch: false
|
||||
hiddenFromRss: false
|
||||
hiddenFromRelated: false
|
||||
summary: This homework assignment focuses on working with lists, loops, tuples, and if statements in Python. It includes three parts - analyzing text complexity, simulating Pikachu's movement, and modeling population changes of bears, berries, and tourists.
|
||||
resources:
|
||||
- name: featured-image
|
||||
src: featured-image.jpg
|
||||
- name: featured-image-preview
|
||||
src: featured-image-preview.jpg
|
||||
toc: true
|
||||
math: false
|
||||
lightgallery: false
|
||||
password:
|
||||
message:
|
||||
repost:
|
||||
enable: true
|
||||
url:
|
||||
|
||||
# See details front matter: https://fixit.lruihao.cn/documentation/content-management/introduction/#front-matter
|
||||
---
|
||||
|
||||
<!--more-->
|
||||
|
||||
## Overview
|
||||
|
||||
This homework is worth 100 points toward your overall homework grade, and is due Thursday, February 15th, 2024 at 11:59:59 pm. You have 1 week to finish this assignment.
|
||||
|
||||
The goal of this assignment is to work with lists, loops, tuples and use if statements. As your programs get longer, you will need to develop some strategies for testing your code. Here are a few simple ones: start testing early, and test small parts of your program by writing a little bit and testing. We will walk you through program construction in the homework description and provide some ideas for testing.
|
||||
|
||||
As always, make sure you follow the program structure guidelines. You will be graded on program correctness as well as good program structure. This includes comments. Minimally, we expect a brief docstring comment block at the start of the submission detailing the purpose and a brief summary (you may also include additional information like your name and date); and docstring comments for each function you define detailing the purpose, inputs, and expected return values. Additional comments have to accompany any complicated sections of your code.
|
||||
|
||||
## Fair Warning About Excess Collaboration
|
||||
|
||||
Please remember to abide by the Collaboration Policy you were given last assignment. It remains in force for this and all assignments this semester. We will be using software that compares all submitted programs, looking for inappropriate similarities. This handles a wide variety of differences between programs, so that if you either (a) took someone else’s program, modified it (or not), and submitted it as your own, (b) wrote a single program with one or more colleagues and submitted modified versions separately as your own work, or (c) submitted (perhaps slightly modified) software submitted in a previous year as your software, this software will mark these submissions as very similar. All of (a), (b), and (c) are beyond what is acceptable in this course — they are violations of the academic integrity policy. Furthermore, this type of copying will prevent you from learning how to solve problems and will hurt you in the long run. The more you write your own code, the more you learn.
|
||||
|
||||
Make sure that you have read the Collaboration Policy for acceptable levels of collaboration and so you know how you can protect yourself. The document can be found on the Course Materials page on Submitty. Penalties for excess collaboration can be as high as:
|
||||
|
||||
- 0 on the homework, and
|
||||
- an additional overall 5% reduction on the semester grade.
|
||||
|
||||
Penalized students will also be prevented from dropping the course. More severe violations, such as stealing someone else’s code, will lead to an automatic F in the course. A student caught in a second academic integrity violation will receive an automatic F.
|
||||
|
||||
By submitting your homework you are asserting that you both (a) understand the academic integrity policy and (b) have not violated it.
|
||||
|
||||
Finally, please note that this policy is in place for the small percentage of problems that will arise in this course. Students who follow the strategies outlined above and use common sense in doing so will not have any trouble with academic integrity.
|
||||
|
||||
## Part 1: How complex is the language used in the text? (40 pts)
|
||||
|
||||
Create a folder for HW 3. Download the zip file `hw3_files.zip` from the Course Materials on Submitty. Put it in this folder and unzip it. You should see a file named `syllables.py` which will be a helper module for this homework. Write your program in the same folder as this file and name it `hw3_part1.py`.
|
||||
|
||||
A few things to get familiar with before solving this part.
|
||||
|
||||
In this part, you must get familiar with a function called `.split()` that takes a piece of text, and converts it to a list of strings. Here is an example run:
|
||||
|
||||
```python
|
||||
>>> line = "Citadel Morning News. News about the Citadel in the morning, pretty self explanatory."
|
||||
>>> m = line.split()
|
||||
>>> m
|
||||
['Citadel', 'Morning', 'News.', 'News', 'about', 'the', 'Citadel', 'in', 'the', 'morning,', 'pretty', 'self', 'explanatory.']
|
||||
```
|
||||
|
||||
You will also need to use the function `find_num_syllables()` from the file `syllables.py` which takes as input an English word as a string and that returns the total number of syllables in that word as an integer. The module works even if the word has punctuation symbols, so you do not need to remove those explicitly. Make sure you import this module appropriately into your program.
|
||||
|
||||
```python
|
||||
>>> find_num_syllables('computer')
|
||||
3
|
||||
>>> find_num_syllables('science')
|
||||
1
|
||||
>>> find_num_syllables('introduction')
|
||||
4
|
||||
```
|
||||
|
||||
Clearly, the second result is incorrect. The module we provided is not a perfect implementation of syllable counting, so you may find errors. It is not your job to fix them, use the module as it is, with errors and all. Do not worry about the mistakes it makes. To properly compute this, we would need to use a Natural Language Processing (NLP) module like NLTK, which we have not installed in this course.
|
||||
|
||||
### Problem specification
|
||||
|
||||
In this part, you will read a paragraph containing multiple English sentences as text from the user. Assume a period marks the end of a sentence. Read the paragraph as a single (long) line of text. Compute and print the following measures corresponding to the overall readability of this text.
|
||||
|
||||
- ASL (average sentence length) is given by the number of words per sentence. Print ASL.
|
||||
- PHW (percent hard words): To compute this first count the number of words of three or more syllables that do not contain a hyphen (-) and three-syllable words that do not end with 'es' or 'ed'. Divide this count by the total number of words in the text and multiply the result by 100 to get a percentage. Print PHW.
|
||||
- Collect all words that are used in the PHW computation in a list exactly as they appear in the input, and print this list.
|
||||
- ASYL (average number of syllables) is given by the total number of syllables divided by the total number of words. Print ASYL.
|
||||
- GFRI is given by the formula 0.4*(ASL + PHW). Print GFRI.
|
||||
- FKRI is given by the formula 206.835-1.015*ASL-86.4*ASYL. Print FKRI.
|
||||
|
||||
Note that the measures GFRI and FKRI are slightly modified versions of well-known readability measures named Gunning-Fog and Flesch Kincaid. In Gunning-fog, the higher the value calculated, the more difficult it is to read a text. For Flesch Kincaid it is the opposite with higher values indicating more easily read text.
|
||||
|
||||
You can find example runs of the program in `hw3_part1_01.txt` and `hw3_part1_02.txt` from `hw3_files.zip`.
|
||||
|
||||
When you are finished, submit your program to Submitty as `hw3_part1.py`. You must use this filename, or your submission will not work in Submitty. You do not have to submit any of the files we have provided.
|
||||
|
||||
## Part 2: Pikachu in the Wild! (40 pts)
|
||||
|
||||
Suppose you have a Pikachu that is standing in the middle of an image, at coordinates (75, 75). Assume the top left corner of the board is (0,0) like in an image.
|
||||
|
||||
We are going to walk a Pikachu around the image looking for other Pokemon. This is a type of simple simulation. First, we will set the parameters of the simulation by asking the user for the number of turns, to run the simulation (starting at turn 0), the name, of your Pikachu and how often, we run into another Pokemon. At this point we enter a simulation loop (while). Your Pikachu walks 5 steps per turn in one of (N)orth, (S)outh, (E)ast or (W)est. Every turn, ask the user for a direction for your Pikachu to walk and move your Pikachu in that direction. You should ignore directions other than N, S, E, W. Every often turns, you meet another Pokemon. Ask the user for a type ((G)round or (W)ater). If it is a ground type, 'G', your Pikachu loses. It turns and runs 10 steps in the direction opposite to the direction in which it was moving before it saw another Pokemon. (If the last direction was not a valid direction, your Pikachu doesn’t move.) If it is a water type, 'W', your Pikachu wins and takes 1 step forward. Anything else means you did not actually see another Pokemon. Keep track of wins, losses, and "No Pokemon" in a list.
|
||||
|
||||
At the end of turn turns report on where your Pikachu ended up and print out its record.
|
||||
|
||||
You must implement at least one function for this program:
|
||||
|
||||
```python
|
||||
move_pokemon((row, column), direction, steps)
|
||||
```
|
||||
|
||||
That returns the next location of the Pikachu as a (row, column) tuple. There is a fence along the boundary of the image. No coordinate can be less than 0 or greater than 150. 0 and 150 are allowed. Make sure your `move_pokemon()` function does not return positions outside of this range.
|
||||
|
||||
You can use the following code to test your `move_pokemon()` function. Feel free to write other functions if you want, but be sure to test them to make sure they work as expected!
|
||||
|
||||
```python
|
||||
from hw3_part2 import move_pokemon
|
||||
|
||||
row = 15
|
||||
column = 10
|
||||
print(move_pokemon((row, column), 'n', 20)) # should print (0, 10)
|
||||
print(move_pokemon((row, column), 'e', 20)) # should print (15, 30)
|
||||
print(move_pokemon((row, column), 's', 20)) # should print (35, 10)
|
||||
print(move_pokemon((row, column), 'w', 20)) # should print (15, 0)
|
||||
|
||||
row = 135
|
||||
column = 140
|
||||
print(move_pokemon((row, column), 'N', 20)) # should print (115, 140)
|
||||
print(move_pokemon((row, column), 'E', 20)) # should print (135, 150)
|
||||
print(move_pokemon((row, column), 'S', 20)) # should print (150, 140)
|
||||
print(move_pokemon((row, column), 'W', 20)) # should print (135, 120)
|
||||
```
|
||||
|
||||
Now, write some code that will call these functions for each command entered and update the location of the Pikachu accordingly.
|
||||
|
||||
Two examples of the program run (how it will look when you run it using Spyder IDE) are provided in files `hw3_part2_01.txt` and `hw3_part2_02.txt` (can be found inside the `hw03_files.zip` file). In `hw3_part2_01.txt`, note that `f` is an invalid direction, so it has no effect on the Pikachu’s state, and `r` is an invalid Pokemon type which gets flagged as a "No Pokemon" in the results list.
|
||||
|
||||
We will test your code with the values from the example files as well as a range of other values. Test your code well and when you are sure that it works, please submit it as a file named `hw3_part2.py` to Submitty for Part 2 of the homework.
|
||||
|
||||
## Part 3: Population Change — with Bears (20 pts)
|
||||
|
||||
You are going to write a program to compute a type of population balance problem similar to the bunnies and foxes you computed in Lab 3. This problem will have bears, berry fields, and tourists. We will just use the word berries to mean the area of the berry fields. We will count the number of bears and tourists, as well.
|
||||
|
||||
Bears need a lot of berries to survive and get ready for winter. So the area of berry fields is a very important part for their population. Berry fields in general spread over time, but if they are trampled too heavily by bears, then they may stop growing and may reduce in size. Tourists are the worst enemy of bears, often habituating them to humans and causing aggressive behavior. Sadly, this can lead to bears being killed to avoid risk to human life.
|
||||
|
||||
Here is how the population of each group is linked to one another from one year to the next. Suppose the variable `bears` stores the number of bears in a given year and `berries` stores the area of the berry fields.
|
||||
|
||||
- The number of tourists in a given year is determined as follows. If there are less than 4 or more than 15 bears, there are no tourists. It is either not interesting enough or too dangerous for them. In other cases, there are 10,000 tourists for each bear up to and including 10 and then 20,000 tourists for each additional bear. It is a great idea to write a function for computing tourists and test it separately.
|
||||
- The number of bears and berries in the next year is determined by the following formulas given the population of bears, berries, and tourists in the given year:
|
||||
|
||||
```python
|
||||
bears_next = berries/(50*(bears+1)) + bears*0.60 - (math.log(1+tourists,10)*0.1)
|
||||
berries_next = (berries*1.5) - (bears+1)*(berries/14) - (math.log(1+tourists,10)*0.05)
|
||||
```
|
||||
|
||||
Remember none of these values can end up being negative. Negative values should be clipped to zero. Also, bears and tourists are integers. The `log` function is in the `math` module.
|
||||
|
||||
You must write a function that takes as input the number of bears, berries, and tourists in a given year and returns the next year’s bears population and berry field area as a tuple.
|
||||
|
||||
```python
|
||||
>>> find_next(5, 1000, 40000)
|
||||
(5, 1071.1984678861438)
|
||||
```
|
||||
|
||||
Then write the main program that reads two values, the current population of bears, and the area of berry fields. Your program then finds and prints the population of all three groups (bears, berries, and tourists) for the first year and another 9 years (10 years total). You must use a loop to do this. The output is formatted such that all values are printed in columns and are aligned to the left within each column. The width of each column is exactly 10 characters (padded with spaces, if necessary). All floating point values need to be printed with exactly one decimal place.
|
||||
|
||||
Once completed, your program should output: the smallest and largest values of the population of bears, berries, and tourists reached in your computation. These values should be output using the same formatting rules as for the population values for each of the years.
|
||||
|
||||
An example of the program run how it will look when you run it using the Spyder IDE is provided in file `hw3_part3_01.txt` (can be found inside the `hw03_files.zip` file). Note that the number of bears may go down to zero and then come back up. Why? Bears from neighboring areas can move in. The min and max values for each of bears, berries, and tourists may come from different years.
|
||||
|
||||
We will test your code with the values from the example file as well as a range of other values. Test your code well and when you are sure that it works, please submit it as a file named `hw3_part3.py` to Submitty for Part 3 of the homework.
|
||||
|
||||
## Supporting Files
|
||||
|
||||
{{< link href="HW3.zip" content="HW3.zip" title="Download HW3.zip" download="HW3.zip" card=true >}}
|
||||
|
||||
## Solutions
|
||||
|
||||
### hw3_part1.py
|
||||
|
||||
```python
|
||||
import syllables
|
||||
|
||||
#user_input = "We hold these truths to be self-evident, that all men are created equal, that they are endowed by their Creator with certain unalienable Rights, that among these are Life, Liberty and the pursuit of Happiness."
|
||||
#user_input = "There is a theory which states that if ever anyone discovers exactly what the Universe is for and why it is here, it will instantly disappear and be replaced by something even more bizarre and inexplicable. There is another theory which states that this has already happened."
|
||||
user_input = str(input("Enter a paragraph => ")).strip()
|
||||
print(user_input)
|
||||
|
||||
# Calculate ASL
|
||||
|
||||
sentences = user_input.split(". ")
|
||||
words_per_sentence = []
|
||||
#print(sentences)
|
||||
|
||||
for sentence in sentences:
|
||||
words = sentence.split()
|
||||
words_per_sentence.append(len(words))
|
||||
|
||||
asl = sum(words_per_sentence) / len(words_per_sentence)
|
||||
#print(asl)
|
||||
|
||||
# Calculate PHW
|
||||
|
||||
words = user_input.split()
|
||||
#print(len(words))
|
||||
|
||||
#find hard words
|
||||
hard_words = []
|
||||
|
||||
for word in words:
|
||||
num_syllables = syllables.find_num_syllables(word)
|
||||
#print(num_syllables)
|
||||
if num_syllables >= 3 and "-" not in word and word[-2:] != "es" and word[-2:] != "ed":
|
||||
#print(words)
|
||||
hard_words.append(word)
|
||||
#print(hard_words)
|
||||
|
||||
#print(hard_words)
|
||||
#print(len(hard_words))
|
||||
#print(words)
|
||||
#print(len(words))
|
||||
phw = len(hard_words) / len(words) * 100
|
||||
|
||||
# Calculate ASYL
|
||||
|
||||
total_syllables = 0
|
||||
|
||||
for word in words:
|
||||
total_syllables += syllables.find_num_syllables(word)
|
||||
|
||||
asyl = total_syllables / len(words)
|
||||
|
||||
# Caclulate GFRI
|
||||
|
||||
gfri = 0.4 * (asl + phw)
|
||||
|
||||
# Calculate FKRI
|
||||
|
||||
fkri = 206.835 - 1.015 * asl - 86.4 * asyl
|
||||
|
||||
print("Here are the hard words in this paragraph:\n" + str(hard_words))
|
||||
print("Statistics: ASL:{:.2f} PHW:{:.2f}% ASYL:{:.2f}".format(asl, phw, asyl))
|
||||
print("Readability index (GFRI): {:.2f}".format(gfri))
|
||||
print("Readability index (FKRI): {:.2f}".format(fkri))
|
||||
```
|
||||
|
||||
### hw3_part2.py
|
||||
|
||||
```python
|
||||
# Get user input
|
||||
|
||||
num_turn = int(input("How many turns? => ").strip())
|
||||
print(num_turn)
|
||||
pikachu_name = str(input("What is the name of your pikachu? => ").strip())
|
||||
print(pikachu_name)
|
||||
often_turn = int(input("How often do we see a Pokemon (turns)? => ").strip())
|
||||
print(str(often_turn) + "\n")
|
||||
|
||||
position = (75, 75)
|
||||
wins, losses, no_pokemon = 0, 0, 0
|
||||
turn_counter = 0
|
||||
records = []
|
||||
last_direction = ""
|
||||
|
||||
# Debugging
|
||||
|
||||
#num_turn = 5
|
||||
#print(num_turn)
|
||||
#pikachu_name = "Piki"
|
||||
#print(pikachu_name)
|
||||
#often_turn = 1
|
||||
#print(often_turn)
|
||||
|
||||
def move_pokemon(coords, direction, steps):
|
||||
global position
|
||||
x,y = coords
|
||||
direction = direction.lower()
|
||||
if direction in ['n', 's', 'e', 'w']:
|
||||
if direction == 'n':
|
||||
x = max(0, x - steps)
|
||||
elif direction == 'e':
|
||||
y = min(150, y + steps)
|
||||
elif direction == 's':
|
||||
x = min(150, x + steps)
|
||||
elif direction == 'w':
|
||||
y = max(0, y - steps)
|
||||
position = (x, y)
|
||||
|
||||
def walk(repeat):
|
||||
global turn_counter, last_direction
|
||||
for _ in range(repeat):
|
||||
direction = input("What direction does " + pikachu_name + " walk? => ").strip()
|
||||
print(direction)
|
||||
direction = direction.lower()
|
||||
if direction in ['n', 's', 'e', 'w']:
|
||||
move_pokemon(position, direction, 5)
|
||||
last_direction = direction
|
||||
turn_counter += 1
|
||||
|
||||
def turn_message(turn_counter, position):
|
||||
print("Turn " + str(turn_counter) + ", " + pikachu_name + " at " + str(position))
|
||||
|
||||
def battle(pokemon_type):
|
||||
global records, position, last_direction
|
||||
if pokemon_type.lower() == 'g':
|
||||
records.append('Lose')
|
||||
opposite_directions = {'n': 's', 's': 'n', 'e': 'w', 'w': 'e'}
|
||||
move_pokemon(position, opposite_directions[last_direction], 10)
|
||||
print(pikachu_name + " runs away to " + str(position))
|
||||
elif pokemon_type.lower() == 'w':
|
||||
records.append('Win')
|
||||
move_pokemon(position, last_direction, 1)
|
||||
print(pikachu_name + " wins and moves to " + str(position))
|
||||
else:
|
||||
records.append('No Pokemon')
|
||||
|
||||
print("Starting simulation, turn 0 " + pikachu_name + " at (75, 75)")
|
||||
|
||||
for i in range(num_turn // often_turn):
|
||||
walk(often_turn)
|
||||
turn_message(turn_counter, position)
|
||||
pokemon_type = input("What type of pokemon do you meet (W)ater, (G)round? => ").strip()
|
||||
print(pokemon_type)
|
||||
battle(pokemon_type)
|
||||
|
||||
if num_turn < often_turn:
|
||||
for i in range(num_turn):
|
||||
direction = input("What direction does " + pikachu_name + " walk? => ").strip()
|
||||
print(direction)
|
||||
direction = direction.lower()
|
||||
if direction in ['n', 's', 'e', 'w']:
|
||||
move_pokemon(position, direction, 5)
|
||||
last_direction = direction
|
||||
turn_counter += 1
|
||||
|
||||
print(pikachu_name + " ends up at " + "(" + str(position[0]) + ", " + str(position[1]) + ")" + ", Record: " + str(records))
|
||||
```
|
||||
|
||||
### hw3_part3.py
|
||||
|
||||
```python
|
||||
import math
|
||||
|
||||
n_bear = input("Number of bears => ").strip()
|
||||
print(n_bear)
|
||||
n_size = input("Size of berry area => ").strip()
|
||||
print(n_size)
|
||||
|
||||
n_bear = int(n_bear)
|
||||
n_size = float(n_size)
|
||||
|
||||
year = 10
|
||||
block_size = 10
|
||||
data = []
|
||||
title = ["Year", "Bears", "Berry", "Tourists"]
|
||||
|
||||
def print_line(b1, b2, b3, b4):
|
||||
b1 = b1 + " " * int(10 - len(b1))
|
||||
b2 = b2 + " " * int(10 - len(b2))
|
||||
b3 = b3 + " " * int(10 - len(b3))
|
||||
b4 = b4 + " " * int(10 - len(b4))
|
||||
print(b1 + b2 + b3 + b4)
|
||||
|
||||
def show_data(data):
|
||||
data_copy = data.copy()
|
||||
for line in data_copy:
|
||||
line[2] = "{:.1f}".format(float(line[2]))
|
||||
line = [str(i) for i in line]
|
||||
print_line(line[0], line[1], line[2], line[3])
|
||||
|
||||
def conclusion(data):
|
||||
#print(data)
|
||||
initial_line = data[0]
|
||||
max_bears, max_berry, max_tourists = initial_line[1], float(initial_line[2]), initial_line[3]
|
||||
min_bears, min_berry, min_tourists = initial_line[1], float(initial_line[2]), initial_line[3]
|
||||
for i in data:
|
||||
max_bears = max(max_bears, i[1])
|
||||
max_berry = max(max_berry, float(i[2]))
|
||||
max_tourists = max(max_tourists, i[3])
|
||||
min_bears = min(min_bears, i[1])
|
||||
min_berry = min(min_berry, float(i[2]))
|
||||
min_tourists = min(min_tourists, i[3])
|
||||
message = []
|
||||
message.append(["Min:", str(min_bears), str(min_berry), str(min_tourists)])
|
||||
message.append(["Max:", str(max_bears), str(max_berry), str(max_tourists)])
|
||||
show_data(message)
|
||||
|
||||
def find_next(bears, berry, tourists):
|
||||
bears_next = berry / (50*(bears+1)) + bears*0.60 - (math.log(1+tourists,10)*0.1)
|
||||
berries_next = (berry*1.5) - (bears+1)*(berry/14) - (math.log(1+tourists,10)*0.05)
|
||||
if berries_next < 0:
|
||||
berries_next = 0
|
||||
return (int(bears_next), berries_next)
|
||||
|
||||
def find_tourists(bears):
|
||||
bears = int(bears)
|
||||
if bears < 4 or bears > 15:
|
||||
tourists = 0
|
||||
elif bears <= 10:
|
||||
tourists = 10000*bears
|
||||
elif bears > 10:
|
||||
tourists = 100000 + 20000*(bears-10)
|
||||
return tourists
|
||||
|
||||
for i in range(year):
|
||||
tourists = find_tourists(n_bear)
|
||||
data.append([i+1, n_bear, n_size, tourists])
|
||||
n_bear, n_size = find_next(n_bear, n_size, tourists)
|
||||
|
||||
print_line(title[0], title[1], title[2], title[3])
|
||||
show_data(data)
|
||||
print()
|
||||
conclusion(data)
|
||||
```
|
||||
BIN
content/en/posts/csci-1100/hw-4/HW4.zip
Normal file
BIN
content/en/posts/csci-1100/hw-4/HW4.zip
Normal file
Binary file not shown.
390
content/en/posts/csci-1100/hw-4/index.md
Normal file
390
content/en/posts/csci-1100/hw-4/index.md
Normal file
@@ -0,0 +1,390 @@
|
||||
---
|
||||
title: CSCI 1100 - Homework 4 - Loops and Lists; Passwords and Quarantine
|
||||
subtitle:
|
||||
date: 2024-03-13T15:15:44-04:00
|
||||
slug: csci-1100-hw-4
|
||||
draft: false
|
||||
author:
|
||||
name: James
|
||||
link: https://www.jamesflare.com
|
||||
email:
|
||||
avatar: /site-logo.avif
|
||||
description: This blog post outlines the requirements and guidelines for completing Homework 4 in the CSCI 1100 - Computer Science 1 course, which consists of two parts focusing on password strength evaluation and analyzing COVID-19 quarantine states using Python programming.
|
||||
keywords: ["CSCI 1100","Computer Science","Python","Password Strength","COVID-19"]
|
||||
license:
|
||||
comment: true
|
||||
weight: 0
|
||||
tags:
|
||||
- CSCI 1100
|
||||
- Homework
|
||||
- RPI
|
||||
- Python
|
||||
- Programming
|
||||
categories:
|
||||
- Programming
|
||||
collections:
|
||||
- CSCI 1100
|
||||
hiddenFromHomePage: false
|
||||
hiddenFromSearch: false
|
||||
hiddenFromRss: false
|
||||
hiddenFromRelated: false
|
||||
summary: This blog post outlines the requirements and guidelines for completing Homework 4 in the CSCI 1100 - Computer Science 1 course, which consists of two parts focusing on password strength evaluation and analyzing COVID-19 quarantine states using Python programming.
|
||||
resources:
|
||||
- name: featured-image
|
||||
src: featured-image.jpg
|
||||
- name: featured-image-preview
|
||||
src: featured-image-preview.jpg
|
||||
toc: true
|
||||
math: true
|
||||
lightgallery: false
|
||||
password:
|
||||
message:
|
||||
repost:
|
||||
enable: true
|
||||
url:
|
||||
|
||||
# See details front matter: https://fixit.lruihao.cn/documentation/content-management/introduction/#front-matter
|
||||
---
|
||||
|
||||
<!--more-->
|
||||
|
||||
## Overview
|
||||
|
||||
This homework is worth 100 points total toward your overall homework grade. It is due in 1 week i.e., on Thursday, February 22, 2024 at 11:59:59 pm. As usual, there will be a mix of autograded points, instructor test case points, and TA graded points. There are two parts in the homework, each to be submitted separately. All parts should be submitted by the deadline or your program will be considered late.
|
||||
|
||||
See the handout for Submission Guidelines and Collaboration Policy for a discussion on grading and on what is considered excessive collaboration. These rules will be in force for the rest of the semester.
|
||||
|
||||
You will need the utilities and data files we provide in `hw4_files.zip`, so be sure to download this file from the Course Materials section of Submitty and unzip it into your directory for HW 4.
|
||||
|
||||
Module `hw4_util.py` is written to help you read information from the files. You do not need to know how functions provided in `hw4_util.py` are implemented (but feel free to examine the code, if you are interested), you can simply use them.
|
||||
|
||||
Final note, you will need to use loops in this assignment. We will leave the choice of the loop type up to you. Please feel free to use while loops or for loops depending on the task and your personal preference.
|
||||
|
||||
## Part 1: Password Strength
|
||||
|
||||
Often when you create a password it is judged for its strength. The estimate of strength is computed by applying several rules — about the length of the password, the presence of certain types of characters, its match against common passwords, and even its match against license plates. In this part of the homework you will implement a few simple strength judgment rules and then determine if a password is to be rejected, or rated poor, fair, good or excellent.
|
||||
|
||||
Your program should start by asking for, and reading in, a password. The program should then evaluate the password based on the following rules. Each rule contributes to a numerical score (which starts at 0):
|
||||
|
||||
1. **Length**: If the password is 6 or 7 characters long then add 1 to the score; if it is 8, 9 or 10 characters long then add 2; and longer than 10 add 3.
|
||||
2. **Case**: If it contains at least two upper case letters and two lower case letters add 2 to the score, while if it contains at least one of each, add 1 to the score.
|
||||
3. **Digits**: If it contains at least two digits add 2 to the score and if it contains at least one digit then add 1.
|
||||
4. **Punctuation**: If it contains at least one of !@#$ add 1 and if it contains at least one of %^&* then add 1 (total possible of 2).
|
||||
5. **NY License**: If it contains three letters (upper or lower case) followed by four digits, then it potentially matches a NY state license plate. In this case, subtract 2 from the score.
|
||||
6. **Common Password**: If the lower case version of the password exactly matches a password found in a list of common passwords, then subtract 3 from the score.
|
||||
|
||||
Whenever a rule is applied that creates a change in the score, generate an explanatory line of output. After applying all the rules, output the score and then convert it to a final strength rating of the password:
|
||||
|
||||
- **Rejected**: the score is less than or equal to 0.
|
||||
- **Poor**: the score is 1 or 2.
|
||||
- **Fair**: the score is 3 or 4
|
||||
- **Good**: the score is 5 or 6
|
||||
- **Excellent**: the score is 7 or above.
|
||||
|
||||
### Notes
|
||||
|
||||
1. For this part and for part 2 you should write functions to keep the code clean, clear and easy to manage.
|
||||
2. We have provided you with a number of examples that show output values and formatting. Please follow these examples closely.
|
||||
3. The common passwords are extracted from a file. One of the utility functions we have provided reads this file and return a list of these passwords. To use this function, start by making sure that `hw4_util.py` and `password_list_top_100.txt` are in the same folder as your code. Then add the line
|
||||
```python
|
||||
import hw4_util
|
||||
```
|
||||
into your program. Finally, call function `hw4_util.part1_get_top` with no arguments. It will return a list of strings containing 100 passwords for you to compare against.
|
||||
5. Submit only your program file `hw4_part1.py`. Do not submit `hw4_util.py`.
|
||||
|
||||
## Part 2: COVID-19 Quarantine States
|
||||
|
||||
The NY State COVID-19 Travel Advisory at [COVID-19 Travel Advisory](https://coronavirus.health.ny.gov/covid-19-travel-advisory) requires that individuals who travel to New York from states that have significant community spread of COVID-19 must self-quarantine for 14 days. “Significant spread” in a state is measured as either:
|
||||
|
||||
- a daily average of more than 10 people per 100,000 residents tested positive in the previous seven days, or
|
||||
- a daily average of more than 10% of tests were positive in the previous seven days.
|
||||
|
||||
We will refer to states having a significant spread as quarantine states. In this part of HW 4 you will use per state data downloaded from [COVID Tracking Project](https://covidtracking.com/) to answer queries about which states were quarantine states and when.
|
||||
|
||||
The data we obtained was downloaded (on October 5, 2023) in the form of a large “comma-separated value” file. This file contains one line per day per state, and there are many fields per line. We have condensed it to a form suitable for a CS 1 homework. The data is shared under the Creative Commons BY 4.0 license which means we can:
|
||||
|
||||
- Share: copy and redistribute the material in any medium or format and
|
||||
- Adapt: remix, transform, and build upon the material for any purpose, even commercially.
|
||||
|
||||
We have provided a simple utility to give you access to the condensed data. To use this (similar to Part 1) you must have the files `hw4_util.py` and `prob2_data.csv` in the same folder as your own code. You must then
|
||||
|
||||
```python
|
||||
import hw4_util
|
||||
```
|
||||
|
||||
into your program. `hw4_util` has a function called `part2_get_week` that accepts a single integer argument, `w`, and returns a list of lists. Argument `w` is the index of a previous week, with `w==1` being the most recent week, `w==2` being the week before that, etc., up to `w==29` which corresponds to 29 weeks ago, all the way back to March 15. The returned list contains one list per state, plus the District of Columbia (DC) and Puerto Rico (PR) — 52 in all. Each state list has 16 items:
|
||||
|
||||
- Item 0 is a string giving the two letter (upper case) state abbreviation. These are correct.
|
||||
- Item 1 is an integer giving the state’s population estimate — from the 2019 Census Bureau estimate [Census Bureau estimate](https://www.census.gov/newsroom/press-kits/2019/national-state-estimates.html).
|
||||
- Items 2-8 are the number of positive tests for that state in each of the seven days of the specified week — most recent day first.
|
||||
- Items 9-15 are the number of negative tests for the state in each of the seven days of
|
||||
- the specified week — most recent day first.
|
||||
|
||||
For example, the list for Alaska for week 1 is
|
||||
|
||||
```text
|
||||
['AK',\
|
||||
731545,\
|
||||
189,147,128,132,106,125,118,\
|
||||
3373,3819,6839,4984,6045,6140,1688]
|
||||
```
|
||||
|
||||
Here is what you need to do for this assignment. Your program, in a loop, must start by asking the user to specify the index for a week, as described above. (You may assume an integer is input as the week number.) A negative number for the week indicates that the program should end. For non-negative numbers, if the data are not available for that week the function will return an empty list; in this case, skip the rest of loop body. Otherwise, after getting the list of lists back, the program should answer one of four different requests for information about that week. Answering the request starts by the user typing in a keyword. The keywords are 'daily', 'pct', 'quar', 'high'. Here’s what the program must do for each request:
|
||||
|
||||
- **'daily'**: Ask the user for the state abbreviation and then output the average daily positive cases per 100,000 people for that state for the given week, accurate to the nearest tenth.
|
||||
- **'pct'**: Ask the user for the state abbreviation and then output the average daily percentage of tests that are positive over the week, accurate to the nearest tenth of a percent.
|
||||
- **'quar'**: Output the list of state abbreviations, alphabetically by two-letter abbreviation, of travel quarantine states for the given week as discussed above. There should be ten state abbreviations per line—call `hw4_util.print_abbreviations` with your list of abbreviations to print the output as required. (Note: every week has at least one quarantine state.)
|
||||
- **'high'**: Output the two-letter abbreviation of the state that had the highest daily average number of positive cases per 100,000 people over the given week, and output this average number, accurate to the nearest tenth.
|
||||
|
||||
Input key words and state abbreviations may be typed in upper or lower case and still match correctly. If a key word is not one of these four or if a state is not found (because its abbreviation is incorrectly typed), output a simple error message and do nothing more in the current loop iteration.
|
||||
|
||||
## Notes
|
||||
|
||||
1. As always, look at the example output we provide and follow it accurately.
|
||||
2. All reported values for numbers of positive and negative test results will be at least 0; however, some may be 0. You may assume, however, that there will never be a week where all days have 0 negative tests.
|
||||
3. Compute the daily percentage of tests that are positives by summing the positive cases for the week, and summing negative cases for the week. If these sums are P and N respectively, then the percentage positive is $P/(P +N) * 100$. This is not exactly the same as the average of daily percentages for a week, but it is easier to compute.
|
||||
4. Submit only your program file `hw4_part2.py`. Do not submit `hw4_util.py`.
|
||||
|
||||
## Supporting Files
|
||||
|
||||
{{< link href="HW4.zip" content="HW4.zip" title="Download HW4.zip" download="HW4.zip" card=true >}}
|
||||
|
||||
## Solution
|
||||
|
||||
### hw4_part1.py
|
||||
|
||||
```python
|
||||
"""
|
||||
This script is used to test password strength based on certain criteria.
|
||||
Author: Jinshan Zhou
|
||||
"""
|
||||
|
||||
import hw4_util
|
||||
|
||||
if __name__ == "__main__":
|
||||
# initialize variables
|
||||
strength = 0
|
||||
report = ""
|
||||
|
||||
# Debugging
|
||||
|
||||
#user_password = "AdmIn123%^%*(&"
|
||||
#user_password = "jaX1234"
|
||||
|
||||
# get user input
|
||||
user_password = str(input("Enter a password => ").strip())
|
||||
|
||||
# print the password for testing purposes
|
||||
print(user_password)
|
||||
|
||||
# get the length of the password
|
||||
length = len(user_password)
|
||||
|
||||
# check the length of the password and update strength and report accordingly
|
||||
if length <= 7 and length >= 6:
|
||||
strength += 1
|
||||
report += "Length: +1\n"
|
||||
elif length >= 8 and length <= 10:
|
||||
strength += 2
|
||||
report += "Length: +2\n"
|
||||
elif length > 10:
|
||||
strength += 3
|
||||
report += "Length: +3\n"
|
||||
|
||||
# count the number of uppercase and lowercase letters in the password
|
||||
num_upper = sum(1 for c in user_password if c.isupper())
|
||||
num_lower = sum(1 for c in user_password if c.islower())
|
||||
|
||||
# check the number of uppercase and lowercase letters and update strength and report accordingly
|
||||
if num_upper >= 2 and num_lower >= 2:
|
||||
strength += 2
|
||||
report += "Cases: +2\n"
|
||||
elif num_upper >= 1 and num_lower >= 1:
|
||||
strength += 1
|
||||
report += "Cases: +1\n"
|
||||
|
||||
# count the number of digits in the password
|
||||
num_digits = sum(1 for c in user_password if c.isdigit())
|
||||
|
||||
# check the number of digits and update strength and report accordingly
|
||||
if num_digits >= 2:
|
||||
strength += 2
|
||||
report += "Digits: +2\n"
|
||||
elif num_digits >= 1:
|
||||
strength += 1
|
||||
report += "Digits: +1\n"
|
||||
|
||||
# check for special characters and update strength and report accordingly
|
||||
if any(c in "!@#$" for c in user_password):
|
||||
strength += 1
|
||||
report += "!@#$: +1\n"
|
||||
if any(c in "%^&*" for c in user_password):
|
||||
strength += 1
|
||||
report += "%^&*: +1\n"
|
||||
|
||||
# check for a specific pattern and update strength and report accordingly
|
||||
if (num_upper + num_lower) == 3 and num_digits == 4 and len(user_password) > 3:
|
||||
check = user_password.replace(user_password[0:3], "")
|
||||
if sum(1 for c in check if c.isdigit()) == 4:
|
||||
strength -= 2
|
||||
report += "License: -2\n"
|
||||
|
||||
# check if the password is in the top 10,000 most common passwords and update strength and report accordingly
|
||||
if user_password.lower() in hw4_util.part1_get_top():
|
||||
strength -= 3
|
||||
report += "Common: -3\n"
|
||||
|
||||
# add the combined score to the report
|
||||
report += "Combined score: " + str(strength) + "\n"
|
||||
|
||||
# check the strength and add the appropriate message to the report
|
||||
if strength <= 0:
|
||||
report += "Password is rejected"
|
||||
elif strength >= 1 and strength <= 2:
|
||||
report += "Password is poor"
|
||||
elif strength >= 3 and strength <= 4:
|
||||
report += "Password is fair"
|
||||
elif strength >= 5 and strength <= 6:
|
||||
report += "Password is good"
|
||||
elif strength >= 7:
|
||||
report += "Password is excellent"
|
||||
|
||||
# print the report
|
||||
print(report)
|
||||
```
|
||||
|
||||
### hw4_part2.py
|
||||
|
||||
```python
|
||||
import hw4_util
|
||||
|
||||
"""
|
||||
hw4_util.part2_get_week(1)[0] ==> ['AK',\
|
||||
731545, 189, 147, 128, 132, 106, 125,\
|
||||
118, 3373, 3819, 6839, 4984, 6045,\
|
||||
6140, 1688]
|
||||
"""
|
||||
|
||||
def find_state(states, state):
|
||||
state = state.upper()
|
||||
found_status = False
|
||||
for i in states:
|
||||
if i[0].upper() == state:
|
||||
found_status = True
|
||||
return i
|
||||
if not found_status:
|
||||
return []
|
||||
|
||||
def get_postive_per_100k(status):
|
||||
population = status[1]
|
||||
total_postive = 0
|
||||
for i in range (2, 9):
|
||||
total_postive += status[i]
|
||||
postive_per_100k = ((total_postive / 7) / population) * 100000
|
||||
return postive_per_100k
|
||||
|
||||
def get_pct_postive_tests(status):
|
||||
num_tested = 0
|
||||
num_postive = 0
|
||||
for i in range (2, 16):
|
||||
num_tested += status[i]
|
||||
for i in range (2, 9):
|
||||
num_postive += status[i]
|
||||
pct_postive_tests = num_postive / num_tested
|
||||
return pct_postive_tests
|
||||
|
||||
def action_valid(request_code):
|
||||
request_code = request_code.lower()
|
||||
allowed_action = ["daily", "pct", "quar", "high"]
|
||||
if request_code in allowed_action:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def quar(week):
|
||||
states = []
|
||||
for i in week:
|
||||
if get_postive_per_100k(i) >= 10 or get_pct_postive_tests(i) >= 0.1:
|
||||
states.append(i[0])
|
||||
states.sort()
|
||||
return states
|
||||
|
||||
def high(week):
|
||||
highest = ""
|
||||
highest_value = 0
|
||||
for i in week:
|
||||
if get_postive_per_100k(i) > highest_value:
|
||||
highest = i[0]
|
||||
highest_value = get_postive_per_100k(i)
|
||||
return highest.upper()
|
||||
|
||||
def show_high(week):
|
||||
highest = high(week)
|
||||
highest_postive_per_100k = get_postive_per_100k(find_state(week, highest))
|
||||
print("State with highest infection rate is", highest)
|
||||
print("Rate is {:.1f} per 100,000 people".format(highest_postive_per_100k))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
index_week = 0 # Initialize index_week
|
||||
print("...")
|
||||
|
||||
while index_week != -1:
|
||||
# Get index of week
|
||||
index_week = input("Please enter the index for a week: ").strip()
|
||||
print(index_week)
|
||||
index_week = int(index_week)
|
||||
|
||||
# Stop if the index is -1
|
||||
if index_week < 0:
|
||||
break
|
||||
|
||||
# Get the week, check if the week is valid
|
||||
week = hw4_util.part2_get_week(index_week).copy()
|
||||
if week == []:
|
||||
print("No data for that week")
|
||||
print("...")
|
||||
continue
|
||||
|
||||
# Get the Action
|
||||
request_code = input("Request (daily, pct, quar, high): ").strip()
|
||||
print(request_code)
|
||||
request_code = request_code.lower()
|
||||
|
||||
# Check if the action is valid
|
||||
if not action_valid(request_code):
|
||||
print("Unrecognized request")
|
||||
print("...")
|
||||
continue
|
||||
|
||||
# Perform the action
|
||||
if request_code == "daily":
|
||||
state = input("Enter the state: ").strip()
|
||||
print(state)
|
||||
state = state.upper()
|
||||
if find_state(week,state) == []:
|
||||
print("State {} not found".format(state))
|
||||
print("...")
|
||||
else:
|
||||
state_data = find_state(week,state)
|
||||
print("Average daily positives per 100K population: {:.1f}".format(get_postive_per_100k(state_data)))
|
||||
print("...")
|
||||
elif request_code == "pct":
|
||||
state = input("Enter the state: ").strip()
|
||||
print(state)
|
||||
state = state.upper()
|
||||
if find_state(week,state) == []:
|
||||
print("State {} not found".format(state))
|
||||
print("...")
|
||||
else:
|
||||
state_data = find_state(week,state)
|
||||
print("Average daily positive percent: {:.1f}".format(get_pct_postive_tests(state_data)*100))
|
||||
print("...")
|
||||
elif request_code == "quar":
|
||||
print("Quarantine states:")
|
||||
hw4_util.print_abbreviations(quar(week))
|
||||
print("...")
|
||||
elif request_code == "high":
|
||||
show_high(week)
|
||||
print("...")
|
||||
```
|
||||
BIN
content/en/posts/csci-1100/hw-5/HW5.zip
Normal file
BIN
content/en/posts/csci-1100/hw-5/HW5.zip
Normal file
Binary file not shown.
473
content/en/posts/csci-1100/hw-5/index.md
Normal file
473
content/en/posts/csci-1100/hw-5/index.md
Normal file
@@ -0,0 +1,473 @@
|
||||
---
|
||||
title: CSCI 1100 - Homework 5 - Lists of Lists; Grids; Path Planning
|
||||
subtitle:
|
||||
date: 2024-03-13T15:36:47-04:00
|
||||
slug: csci-1100-hw-5
|
||||
draft: false
|
||||
author:
|
||||
name: James
|
||||
link: https://www.jamesflare.com
|
||||
email:
|
||||
avatar: /site-logo.avif
|
||||
description: This blog post provides an overview of a homework assignment for CSCI 1100 - Computer Science 1, focusing on lists of lists, grids, and path planning using Python. It includes problem descriptions, guidelines, and example outputs for each part of the assignment.
|
||||
keywords: ["Python","Computer Science","Lists","Grids","Path Planning"]
|
||||
license:
|
||||
comment: true
|
||||
weight: 0
|
||||
tags:
|
||||
- CSCI 1100
|
||||
- Homework
|
||||
- RPI
|
||||
- Python
|
||||
- Programming
|
||||
categories:
|
||||
- Programming
|
||||
collections:
|
||||
- CSCI 1100
|
||||
hiddenFromHomePage: false
|
||||
hiddenFromSearch: false
|
||||
hiddenFromRss: false
|
||||
hiddenFromRelated: false
|
||||
summary: This blog post provides an overview of a homework assignment for CSCI 1100 - Computer Science 1, focusing on lists of lists, grids, and path planning using Python. It includes problem descriptions, guidelines, and example outputs for each part of the assignment.
|
||||
resources:
|
||||
- name: featured-image
|
||||
src: featured-image.jpg
|
||||
- name: featured-image-preview
|
||||
src: featured-image-preview.jpg
|
||||
toc: true
|
||||
math: false
|
||||
lightgallery: false
|
||||
password:
|
||||
message:
|
||||
repost:
|
||||
enable: true
|
||||
url:
|
||||
|
||||
# See details front matter: https://fixit.lruihao.cn/documentation/content-management/introduction/#front-matter
|
||||
---
|
||||
|
||||
<!--more-->
|
||||
|
||||
## Overview
|
||||
|
||||
This homework is worth 100 points total toward your overall homework grade. It is due Thursday, February 29, 2024 at 11:59:59 pm. As usual, there will be a mix of autograded points, instructor test case points, and TA graded points. There are two parts to the homework, each to be submitted separately. Both parts should be submitted by the deadline or your program will be considered late.
|
||||
|
||||
See the handout for Submission Guidelines and Collaboration Policy for a discussion on grading and on what is considered excessive collaboration. These rules will be in force for the rest of the semester.
|
||||
|
||||
You will need the data files we provide in `hw5_files.zip`, so be sure to download this file from the Course Materials section of Submitty and unzip it into your directory for HW 5. The zip file contains utility code, data files and example input / output for your program.
|
||||
|
||||
## Problem Description
|
||||
|
||||
Many problems in computer science and in engineering are solved on a two-dimensional numerical grid using techniques that are variously called “gradient ascent” (or “descent”), greedy search, or hill-climbing. We are going to study a simplified version of this using hand-generated elevation data.
|
||||
|
||||
The main representation we need is a list of lists of “heights” (also called “elevations”, but we will use the simpler term “heights” here). For example:
|
||||
|
||||
```python
|
||||
grid = [[15, 16, 18, 19, 12, 11],
|
||||
[13, 19, 23, 21, 16, 12],
|
||||
[12, 15, 17, 19, 22, 10],
|
||||
[10, 14, 16, 13, 9, 6]]
|
||||
```
|
||||
|
||||
This grid has four lists of six integer entries each. Each entry represents a height — e.g., meters above sea level — and the heights are measured at regularly-spaced intervals, which could be as small as centimeters or as large as kilometers. The USGS, United States Geological Survey, maintains and distributes elevation data like this, but private companies do so as well. Such data are important for evaluating water run-off, determining placement of wind turbines, and planning roads and construction, just to name a few uses. We are going to use the analogy of planning hiking paths on a plot of land.
|
||||
|
||||
Questions we might ask about this data include:
|
||||
|
||||
1. What is the highest point (greatest height)? This is also called the “global maximum” because it is the greatest value in the data. In our example, this height value is 23 and it occurs in list 1, entry 2. We will refer to this as row 1, column 2 (or “col 2”), and write these values as a tuple, (1, 2), where we assume the first value is the row and the second is the column. We refer to (1, 2) as a “location”.
|
||||
2. Are there “local maxima” in the data? These are entries whose value is greater than their immediately surrounding values, but smaller than the global maximum. In our example there is a local maxima of 22 at location (2, 4).
|
||||
3. Starting at a given location, what is the best path to the global maxima? This is a tricky question because we need to define “best path”. Here is a simple one: can we start at a given location and only take the steepest route and get to the global maximum (can we hike up to the “peak”)? For example, if we start at (3, 0) then the path through locations (3, 0), (3, 1), (3, 2), (2, 2), (1, 2) follows the steepest route and reaches the top. This is a “gradient ascent” method. But, if we start at location (3, 5) then we will generate the route (3, 5), (3, 4), (2, 4), but then there is no way to continue up to reach the global maximum.
|
||||
|
||||
There are many more questions that we can ask and answer. Some of them can be solved easily, while others require sophisticated algorithms and expensive computations.
|
||||
|
||||
Before getting started on the actual assignment, it is important to define the notion of a “neighbor” location in the grid — one that we are allowed to step to from a given location. For our purposes, from location (r, c), the neighbor locations are (r-1, c), (r, c-1), (r, c+1), and (r+1, c).
|
||||
|
||||
In other words, a neighbor location must be in the same row or the same column as the current location. Finally, neighbors cannot be outside the grid, so for example at location (r, 0) only (r-1, 0), (r, 1), (r+1, 0) are allowed as neighbors.
|
||||
|
||||
## Getting Started
|
||||
|
||||
Please download `hw5_files.zip` and place all the files in the same folder that you are going to write your solutions. Files `hw5_util.py` and `hw5_grids.txt` are quite important: `hw5_util.py` contains utility functions to read grids and starting locations from `hw5_grids.txt`. In particular:
|
||||
|
||||
- `hw5_util.num_grids()` returns the number of different grids in the data.
|
||||
- `hw5_util.get_grid(n)` returns grid n, where n==1 is the first grid and n == `hw5_util.num_grids()` is the last.
|
||||
- `hw5_util.get_start_locations(n)` returns a list of tuples giving one or more starting locations to consider for grid n.
|
||||
- `hw5_util.get_path(n)` returns a list of tuples giving a possible path for grid n.
|
||||
|
||||
We suggest you start by playing around with these functions and printing out what you get so that you are sure you understand.
|
||||
|
||||
You may assume the following about the data:
|
||||
|
||||
1. The grid has at least two rows.
|
||||
2. Each row has at least two entries (columns) and each row has the same number of columns.
|
||||
3. All heights are positive integers.
|
||||
4. The start locations are all within the range of rows and columns of the grid.
|
||||
5. The locations on the path are all within the range of rows and columns of the grid.
|
||||
|
||||
## Part 1
|
||||
|
||||
Write a python program, `hw5_part1.py` that does the following:
|
||||
|
||||
1. Asks the user for a grid number and loops until one in the proper range is provided. Denote the grid number as n.
|
||||
2. Gets grid n.
|
||||
3. Asks the user if they want to print the grid. A single character response of 'Y' or 'y' should cause the grid to be printed. For anything else the grid should not be printed. When printing, you may assume that the elevations are less than 1,000 meters. See the example output.
|
||||
4. Gets the start locations associated with grid n and for each it prints the set of neighbor locations that are within the boundaries of the grid. For example if grid n has 8 rows and 10 columns, and the list of start locations is `[(4, 6), (0, 3), (7, 9)]` then the output should be:
|
||||
|
||||
```plaintext
|
||||
Neighbors of (4, 6): (3, 6) (4, 5) (4, 7) (5, 6)
|
||||
Neighbors of (0, 3): (0, 2) (0, 4) (1, 3)
|
||||
Neighbors of (7, 9): (6, 9) (7, 8)
|
||||
```
|
||||
|
||||
Very important: we strongly urge you to write a function called `get_nbrs` that takes as parameters a row, col location, together with the number of rows and columns in the grid, and returns a list of tuples containing the locations that are neighbors of the row, col location and are within the bounds of the grid. You will make use of this function frequently.
|
||||
|
||||
5. Gets the suggested path, decides if it is a valid path (each location is a neighbor of the next), and then calculates the total downward elevation change and the total upward elevation change. For example using the grid above, if the path is `(3, 1), (3, 0), (2, 0), (1, 0), (1, 1), (0, 1), (0, 2), (1, 2)` the downward elevation changes are from (3, 1) to (3, 0) (change of 4) and from (1, 1) to (0, 1) (change) of 3 for a total of 7, and the upward elevation changes are from (3, 0) to (2, 0), from (2, 0) to (1, 0), from (1, 0) to (1, 1), from (0, 1) to (0, 2) and from (0, 2) to (1, 2) for a total of (2 + 1 + 6 + 2 + 5) = 16). The output should be:
|
||||
|
||||
```plaintext
|
||||
Valid path
|
||||
Downward 7
|
||||
Upward 16
|
||||
```
|
||||
|
||||
If the path is invalid, the code should print `Path: invalid step from point1 to point2.` Here point1 and point2 are the tuples representing the start and end of an invalid step.
|
||||
|
||||
Submit just the file `hw5_part1.py` and nothing else.
|
||||
|
||||
## Part 2
|
||||
|
||||
Revise your solution to Part 1 and submit it as `hw5_part2.py`. The program should again ask the user for the grid number, but it should not print the grid. Next, it should find and output the location and height of the global maximum height. For example for the simple example grid, the output should be `global max: (1, 2) 23`. You may assume without checking that the global maximum is unique.
|
||||
|
||||
The main task of Part 2 is to find and output two paths from each start location for the grid. The first is the steepest path up, and the second is the most gradual path up. The steps on each path must be between neighboring locations as in Part 1. Also, on each path no steps to a location at the same height or lower are allowed, and the step size (the change in height) can be no more than a maximum step height (difference between heights at the new location and at the current location). Your code must ask the user for the value of this maximum step height.
|
||||
|
||||
Next, determine for each path if it reaches the location of the global maximum height in the grid, a local maximum, or neither. The latter can happen at a location where the only upward steps are too high relative to the height at the current location. Of course, true hiking paths can go both up and down, but finding an “optimal path” in this more complicated situation requires much more sophisticated algorithms than we are ready to develop here.
|
||||
|
||||
As an example of the required results, here is the same grid as above:
|
||||
|
||||
```python
|
||||
grid = [[15, 16, 18, 19, 12, 11],
|
||||
[13, 19, 23, 21, 16, 12],
|
||||
[12, 15, 17, 19, 20, 10],
|
||||
[10, 14, 16, 13, 9, 6]]
|
||||
```
|
||||
|
||||
Starting at location (3, 0) with a maximum height change of 4, the steepest path is (3, 0), (3, 1), (3, 2), (2, 2), (2, 3), (1, 3), (1, 2), while the most gradual path is (3, 0), (2, 0), (1, 0), (0, 0), (0, 1), (0, 2), (0, 3), (1, 3), (1, 2). Both reach the global maximum, and both avoid stepping to the global maximum the first time they are close because the step height is too large. Note that both the steepest and most gradual paths from location (3, 5) would end at the local maximum (2, 4). The steepest path would end after four steps (five locations on the path) and the most gradual would end after six steps (seven locations on the path). If the max step height were only 3, then both paths from (3, 5) would stop at location(3, 4) before any maximum is reached.
|
||||
|
||||
Paths should be output with 5 locations per line, for example:
|
||||
|
||||
```plaintext
|
||||
steepest path
|
||||
(3, 0) (2, 0) (1, 0) (0, 0) (0, 1)
|
||||
(0, 2) (0, 3) (1, 3) (1, 2)
|
||||
global maximum
|
||||
```
|
||||
|
||||
See the example output for further details.
|
||||
|
||||
Finally, if requested by the user, output a grid — we’ll call it the “path grid” — giving at each location the number of paths that include that location. This can be handled by forming a new list of lists, where each entry represents a count — initialized to 0. For each path and for each location (i, j) on the path, the appropriate count in the list of lists should be incremented. At the end, after all paths have been generated and added to the counts, output the grid. In this output, rather than printing a 0 for a locations that are not on any path, please output a '.'; this will make the output clearer. See the example output.
|
||||
|
||||
## Notes
|
||||
|
||||
1. In deciding the choice of next locations on a path, if there is a tie, then pick the one that is earlier in the list produced by your `get_nbrs` function. For example starting at (0, 5) with elevation 11 in the above example grid, both (0, 4) and (1, 5) have elevation 12. In this case (0, 4) would be earlier in the `get_nbrs` list and therefore chosen as the next location on the path.
|
||||
2. Please do not work on the path grid output — the last step — until you are sure you have everything else working.
|
||||
3. Both the most gradual and steepest paths are examples of greedy algorithms where the best choice available is made at every step and never reconsidered. More sophisticated algorithms would consider some form of backtracking where decisions are undone and alternatives reconsidered.
|
||||
|
||||
## Supporting Files
|
||||
|
||||
{{< link href="HW5.zip" content="HW5.zip" title="Download HW5.zip" download="HW5.zip" card=true >}}
|
||||
|
||||
## Solution
|
||||
|
||||
### hw5_part1.py
|
||||
|
||||
```python
|
||||
import hw5_util
|
||||
|
||||
def show_grid(grid):
|
||||
""" Display the grid in a human-readable format. """
|
||||
display = ""
|
||||
for row in grid:
|
||||
for cell in row:
|
||||
display += " {:2d}".format(int(cell))
|
||||
display += "\n"
|
||||
print(display, end="")
|
||||
|
||||
def find_grid_size(grid):
|
||||
""" Find the size of the grid. """
|
||||
return (len(grid), len(grid[0]))
|
||||
|
||||
def find_neighbors(point, grid):
|
||||
""" Find the neighbors of a point in the grid. """
|
||||
neighbors = []
|
||||
y, x = point
|
||||
max_y, max_x = find_grid_size(grid)
|
||||
neighbors.append((y-1, x)) if y-1 >= 0 else None
|
||||
neighbors.append((y, x-1)) if x-1 >= 0 else None
|
||||
neighbors.append((y, x+1)) if x+1 < max_x else None
|
||||
neighbors.append((y+1, x)) if y+1 < max_y else None
|
||||
neighbors.append((y-1, x-1)) if y-1 >= 0 and x-1 >= 0 else None
|
||||
neighbors.append((y-1, x+1)) if y-1 >= 0 and x+1 < max_x else None
|
||||
neighbors.append((y+1, x-1)) if y+1 < max_y and x-1 >= 0 else None
|
||||
neighbors.append((y+1, x+1)) if y+1 < max_y and x+1 < max_x else None
|
||||
return neighbors
|
||||
|
||||
def show_neighbors(start_locations, grid):
|
||||
""" Display the neighbors of the start locations."""
|
||||
for i in start_locations:
|
||||
neighbors = ""
|
||||
for j in find_neighbors(i, grid):
|
||||
neighbors += str(j) + " "
|
||||
neighbors = neighbors.strip()
|
||||
print("Neighbors of {}: {}".format(i, neighbors))
|
||||
|
||||
def path_validator(path, grid):
|
||||
""" Validate the path. """
|
||||
result = (True, "Valid path")
|
||||
for i in range(1, len(path)):
|
||||
if path[i] not in find_neighbors(path[i-1], grid):
|
||||
result = (False, "Path: invalid step from {} to {}".format(path[i-1], path[i]))
|
||||
break
|
||||
return result
|
||||
|
||||
def movement_status(path, grid):
|
||||
""" Determine the movement status of the path. """
|
||||
downward = 0
|
||||
upward = 0
|
||||
for i in range(1, len(path)):
|
||||
change = grid[path[i][0]][path[i][1]] - grid[path[i-1][0]][path[i-1][1]]
|
||||
if change > 0:
|
||||
upward += change
|
||||
elif change < 0:
|
||||
downward += change
|
||||
return (downward, upward)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
grid_index = input("Enter a grid index less than or equal to 3 (0 to end): ").strip()
|
||||
#grid_index = 1 # Debugging
|
||||
print(grid_index)
|
||||
grid_index = int(grid_index)
|
||||
grid = hw5_util.get_grid(int(grid_index))
|
||||
#print(grid) # Debugging
|
||||
"""
|
||||
grid = [[15, 16, 18, 19, 12, 11],\
|
||||
[13, 19, 23, 21, 16, 12],\
|
||||
[12, 15, 17, 19, 20, 10],\
|
||||
[10, 14, 16, 13, 9, 6]]
|
||||
"""
|
||||
|
||||
print_gride = input("Should the grid be printed (Y or N): ").strip()
|
||||
#print_gride = "Y" # Debugging
|
||||
print(print_gride)
|
||||
if print_gride.upper() == "Y":
|
||||
print("Grid {}".format(grid_index))
|
||||
show_grid(grid)
|
||||
|
||||
print("Grid has {} rows and {} columns".format(find_grid_size(grid)[0], find_grid_size(grid)[1]))
|
||||
|
||||
start_locations = hw5_util.get_start_locations(grid_index)
|
||||
"""start_locations = [(3, 3), (3, 0), (3, 5), (0, 2)]"""
|
||||
show_neighbors(start_locations, grid)
|
||||
"""find_neighbors((3, 3), grid) = [(2, 3), (3, 2), (3, 4)]"""
|
||||
|
||||
suggested_path = hw5_util.get_path(grid_index)
|
||||
#print("Suggested path: ", suggested_path) # Debugging
|
||||
"""Suggested path: [(3, 1), (3, 0), (2, 0), (1, 0), (1, 1), (0, 1), (0, 2), (1, 2)]"""
|
||||
print(path_validator(suggested_path, grid)[1])
|
||||
"""
|
||||
(True, 'Valid path')
|
||||
(False, 'Path: invalid step from (2, 4) to (3, 3)')
|
||||
"""
|
||||
|
||||
if path_validator(suggested_path, grid)[0]:
|
||||
downward, upward = movement_status(suggested_path, grid)
|
||||
downward = abs(downward)
|
||||
print("Downward {}\nUpward {}".format(downward, upward))
|
||||
```
|
||||
|
||||
### hw5_part2.py
|
||||
|
||||
```python
|
||||
import hw5_util
|
||||
|
||||
def find_height(point, grid):
|
||||
""" Find the height of a point in the grid. """
|
||||
return grid[point[0]][point[1]]
|
||||
|
||||
def find_grid_size(grid):
|
||||
""" Find the size of the grid. """
|
||||
return (len(grid), len(grid[0]))
|
||||
|
||||
def find_global_max(grid):
|
||||
""" Find the global maximum of the grid. """
|
||||
max_height = ((0,0), 0)
|
||||
for i in range(len(grid)):
|
||||
for j in range(len(grid[i])):
|
||||
if grid[i][j] > max_height[1]:
|
||||
max_height = ((i,j), grid[i][j])
|
||||
return max_height
|
||||
|
||||
def find_neighbors(point, grid):
|
||||
""" Find the neighbors of a point in the grid. """
|
||||
neighbors = []
|
||||
y, x = point
|
||||
max_y, max_x = find_grid_size(grid)
|
||||
neighbors.append((y-1, x)) if y-1 >= 0 else None
|
||||
neighbors.append((y, x-1)) if x-1 >= 0 else None
|
||||
neighbors.append((y, x+1)) if x+1 < max_x else None
|
||||
neighbors.append((y+1, x)) if y+1 < max_y else None
|
||||
return neighbors
|
||||
|
||||
def find_steepest_path(start, grid, max_step):
|
||||
""" Find the steepest path."""
|
||||
path = [start]
|
||||
current = start
|
||||
|
||||
while True:
|
||||
neighbors = find_neighbors(current, grid)
|
||||
next_step = None
|
||||
max_height_diff = 0
|
||||
|
||||
for n in neighbors:
|
||||
height_diff = find_height(n, grid) - find_height(current, grid)
|
||||
if height_diff > 0 and height_diff <= max_step and height_diff > max_height_diff:
|
||||
next_step = n
|
||||
max_height_diff = height_diff
|
||||
|
||||
if next_step is None:
|
||||
break
|
||||
|
||||
path.append(next_step)
|
||||
current = next_step
|
||||
return path
|
||||
|
||||
def find_gradual_path(start, grid, max_step):
|
||||
"""" Find the most gradual path."""
|
||||
path = [start]
|
||||
current = start
|
||||
|
||||
while True:
|
||||
neighbors = find_neighbors(current, grid)
|
||||
next_step = None
|
||||
min_height_diff = float("inf")
|
||||
|
||||
for n in neighbors:
|
||||
height_diff = find_height(n, grid) - find_height(current, grid)
|
||||
if height_diff > 0 and height_diff <= max_step and height_diff < min_height_diff:
|
||||
next_step = n
|
||||
min_height_diff = height_diff
|
||||
|
||||
if next_step is None:
|
||||
break
|
||||
|
||||
path.append(next_step)
|
||||
current = next_step
|
||||
return path
|
||||
|
||||
def show_path(path):
|
||||
"""" Display the path."""
|
||||
display = ""
|
||||
counter = 0
|
||||
for i in range(len(path)):
|
||||
if counter == 5:
|
||||
display += "\n"
|
||||
counter = 0
|
||||
display += "({}, {}) ".format(path[i][0], path[i][1])
|
||||
counter += 1
|
||||
print(display)
|
||||
|
||||
def find_path_end(path, grid):
|
||||
""" Find the end of the path."""
|
||||
end_point = path[-1]
|
||||
end_height = find_height(end_point, grid)
|
||||
|
||||
max_point, max_height = find_global_max(grid)
|
||||
|
||||
if end_height == max_height:
|
||||
return "global maximum"
|
||||
|
||||
neighbors = find_neighbors(end_point, grid)
|
||||
is_local_max = True
|
||||
for n in neighbors:
|
||||
if find_height(n, grid) > end_height:
|
||||
is_local_max = False
|
||||
break
|
||||
|
||||
if is_local_max:
|
||||
return "local maximum"
|
||||
else:
|
||||
return "no maximum"
|
||||
|
||||
def build_path_grid(grid, paths):
|
||||
""" Build the path grid."""
|
||||
rows, cols = find_grid_size(grid)
|
||||
path_grid = [[0] * cols for _ in range(rows)]
|
||||
|
||||
for path in paths:
|
||||
for point in path:
|
||||
y, x = point
|
||||
path_grid[y][x] += 1
|
||||
|
||||
return path_grid
|
||||
|
||||
def print_path_grid(path_grid):
|
||||
""" Display the path grid."""
|
||||
for i in range(len(path_grid)):
|
||||
row = " "
|
||||
for j in range(len(path_grid[i])):
|
||||
if path_grid[i][j] > 0:
|
||||
row += str(path_grid[i][j]) + " "
|
||||
else:
|
||||
row += "." + " "
|
||||
print(row.rstrip())
|
||||
|
||||
def print_path(path, path_type):
|
||||
""" Display the path."""
|
||||
print("{} path".format(path_type))
|
||||
show_path(path)
|
||||
print(find_path_end(path, grid))
|
||||
|
||||
if __name__ == "__main__":
|
||||
grid_index = input("Enter a grid index less than or equal to 3 (0 to end): ").strip()
|
||||
#grid_index = 1 # Debugging
|
||||
print(grid_index)
|
||||
grid_index = int(grid_index)
|
||||
grid = hw5_util.get_grid(int(grid_index))
|
||||
"""
|
||||
grid = [[15, 16, 18, 19, 12, 11],\
|
||||
[13, 19, 23, 21, 16, 12],\
|
||||
[12, 15, 17, 19, 20, 10],\
|
||||
[10, 14, 16, 13, 9, 6]]
|
||||
"""
|
||||
start_locations = hw5_util.get_start_locations(grid_index)
|
||||
|
||||
max_step_height = input("Enter the maximum step height: ").strip()
|
||||
#max_step_height = "4" # Debugging
|
||||
print(max_step_height)
|
||||
max_step_height = int(max_step_height)
|
||||
|
||||
print_path_grid_choice = input("Should the path grid be printed (Y or N): ").strip()
|
||||
#print_path_grid_choice = "Y" # Debugging
|
||||
print(print_path_grid_choice)
|
||||
print_path_grid_choice = print_path_grid_choice.upper()
|
||||
|
||||
print("Grid has {} rows and {} columns".format(find_grid_size(grid)[0], find_grid_size(grid)[1]))
|
||||
print("global max: {} {}".format(find_global_max(grid)[0], find_global_max(grid)[1]))
|
||||
|
||||
print("===")
|
||||
|
||||
paths = []
|
||||
|
||||
for start in start_locations:
|
||||
steepest = find_steepest_path(start, grid, max_step_height)
|
||||
gradual = find_gradual_path(start, grid, max_step_height)
|
||||
|
||||
print_path(steepest, "steepest")
|
||||
print("...")
|
||||
print_path(gradual, "most gradual")
|
||||
print("===")
|
||||
|
||||
paths.append(steepest)
|
||||
paths.append(gradual)
|
||||
|
||||
path_grid = build_path_grid(grid, paths)
|
||||
print("Path grid")
|
||||
print_path_grid(path_grid)
|
||||
```
|
||||
BIN
content/zh-cn/posts/csci-1100/hw-3/HW3.zip
Normal file
BIN
content/zh-cn/posts/csci-1100/hw-3/HW3.zip
Normal file
Binary file not shown.
435
content/zh-cn/posts/csci-1100/hw-3/index.md
Normal file
435
content/zh-cn/posts/csci-1100/hw-3/index.md
Normal file
@@ -0,0 +1,435 @@
|
||||
---
|
||||
title: CSCI 1100 - 作业 3 - 循环、元组、列表和条件语句
|
||||
subtitle:
|
||||
date: 2024-03-13T14:28:32-04:00
|
||||
slug: csci-1100-hw-3
|
||||
draft: false
|
||||
author:
|
||||
name: James
|
||||
link: https://www.jamesflare.com
|
||||
email:
|
||||
avatar: /site-logo.avif
|
||||
description: 这次家庭作业的重点是在 Python 中运用列表、循环、元组和条件语句。它包括三个部分 - 分析文本复杂度、模拟皮卡丘的移动,以及模拟熊、浆果和游客的种群变化。
|
||||
keywords: ["Python","循环","元组","列表","条件语句"]
|
||||
license:
|
||||
comment: true
|
||||
weight: 0
|
||||
tags:
|
||||
- CSCI 1100
|
||||
- 作业
|
||||
- RPI
|
||||
- Python
|
||||
- 编程
|
||||
categories:
|
||||
- 编程语言
|
||||
collections:
|
||||
- CSCI 1100
|
||||
hiddenFromHomePage: false
|
||||
hiddenFromSearch: false
|
||||
hiddenFromRss: false
|
||||
hiddenFromRelated: false
|
||||
summary: 这次家庭作业的重点是在 Python 中运用列表、循环、元组和条件语句。它包括三个部分 - 分析文本复杂度、模拟皮卡丘的移动,以及模拟熊、浆果和游客的种群变化。
|
||||
resources:
|
||||
- name: featured-image
|
||||
src: featured-image.jpg
|
||||
- name: featured-image-preview
|
||||
src: featured-image-preview.jpg
|
||||
toc: true
|
||||
math: false
|
||||
lightgallery: false
|
||||
password:
|
||||
message:
|
||||
repost:
|
||||
enable: true
|
||||
url:
|
||||
|
||||
# 有关详细的 front matter 设置,请参阅: https://fixit.lruihao.cn/documentation/content-management/introduction/#front-matter
|
||||
---
|
||||
|
||||
<!--more-->
|
||||
|
||||
## 概述
|
||||
|
||||
这次家庭作业占你总家庭作业成绩的 100 分,截止日期为 2024 年 2 月 15 日星期四晚上 11:59:59。你有 1 周时间完成这个作业。
|
||||
|
||||
这个作业的目标是运用列表、循环、元组和条件语句。随着你的程序变得越来越长,你需要开发一些策略来测试你的代码。这里有一些简单的策略:尽早开始测试,通过编写一点点代码并进行测试来测试程序的小部分。我们将在家庭作业描述中引导你构建程序,并提供一些测试的想法。
|
||||
|
||||
和往常一样,确保你遵循程序结构指南。你将根据程序的正确性以及良好的程序结构进行评分。这包括注释。最低限度,我们期望在提交的开始有一个简短的 docstring 注释块,详细说明目的和简要总结 (你也可以包括额外的信息,如你的姓名和日期); 以及每个你定义的函数的 docstring 注释,详细说明目的、输入和预期的返回值。额外的注释必须伴随你代码中任何复杂的部分。
|
||||
|
||||
## 关于过度合作的警告
|
||||
|
||||
请记住遵守你在上一个作业中得到的合作政策。它在本学期的这个和所有作业中保持有效。我们将使用软件比较所有提交的程序,寻找不适当的相似之处。这个软件可以处理程序之间的各种差异,所以如果你 (a) 拿了别人的程序,修改了 (或没有修改),并作为你自己的程序提交, (b) 与一个或多个同学一起编写了一个程序,并作为你自己的作业分别提交了修改后的版本,或者 (c) 提交了 (可能稍作修改) 前一年提交的软件作为你的软件,这个软件会将这些提交标记为非常相似。 (a)、 (b) 和 (c) 都超出了本课程可接受的范围 - 它们违反了学术诚信政策。此外,这种抄袭会阻碍你学习如何解决问题,从长远来看会伤害你。你编写自己的代码越多,你学到的就越多。
|
||||
|
||||
确保你已经阅读了合作政策,了解可接受的合作水平,以及你如何保护自己。该文件可以在 Submitty 的课程材料页面上找到。过度合作的处罚可高达:
|
||||
|
||||
- 家庭作业得 0 分,以及
|
||||
- 学期成绩额外减少 5%。
|
||||
|
||||
受到处罚的学生也将被禁止退出该课程。更严重的违规行为,如盗用他人代码,将导致该课程自动得 F。第二次被发现违反学术诚信的学生将自动得到 F。
|
||||
|
||||
通过提交你的家庭作业,你断言你 (a) 理解学术诚信政策,并且 (b) 没有违反它。
|
||||
|
||||
最后,请注意,这项政策是针对本课程中将出现的少数问题而制定的。遵循上述策略并在这样做时运用常识的学生不会在学术诚信方面遇到任何麻烦。
|
||||
|
||||
## 第 1 部分: 文本中使用的语言有多复杂? (40 分)
|
||||
|
||||
为 HW 3 创建一个文件夹。从 Submitty 上的课程材料下载 zip 文件 `hw3_files.zip`。把它放在这个文件夹里并解压。你应该看到一个名为 `syllables.py` 的文件,它将是这个家庭作业的辅助模块。在与此文件相同的文件夹中编写你的程序,并将其命名为 `hw3_part1.py`。
|
||||
|
||||
在解决这一部分之前要熟悉的几件事。
|
||||
|
||||
在这一部分,你必须熟悉一个名为 `.split()` 的函数,它接受一段文本,并将其转换为一个字符串列表。下面是一个示例运行:
|
||||
|
||||
```python
|
||||
>>> line = "Citadel Morning News. News about the Citadel in the morning, pretty self explanatory."
|
||||
>>> m = line.split()
|
||||
>>> m
|
||||
['Citadel', 'Morning', 'News.', 'News', 'about', 'the', 'Citadel', 'in', 'the', 'morning,', 'pretty', 'self', 'explanatory.']
|
||||
```
|
||||
|
||||
你还需要使用文件 `syllables.py` 中的函数 `find_num_syllables()`,它接受一个英语单词作为字符串输入,并返回该单词中音节的总数作为整数。即使单词有标点符号,该模块也能工作,所以你不需要显式地删除它们。确保你在程序中适当地导入这个模块。
|
||||
|
||||
```python
|
||||
>>> find_num_syllables('computer')
|
||||
3
|
||||
>>> find_num_syllables('science')
|
||||
1
|
||||
>>> find_num_syllables('introduction')
|
||||
4
|
||||
```
|
||||
|
||||
显然,第二个结果是不正确的。我们提供的模块不是一个完美的音节计数实现,所以你可能会发现错误。修复它们不是你的工作,按原样使用该模块,包括其中的错误。不要担心它犯的错误。要正确地计算这个,我们需要使用一个自然语言处理 (NLP) 模块,如 NLTK,我们在本课程中没有安装。
|
||||
|
||||
### 问题规范
|
||||
|
||||
在这一部分,你将从用户那里读取一个包含多个英语句子的文本段落。假设句号标志着句子的结束。将段落读取为一行 (长) 文本。计算并打印与该文本的整体可读性相对应的以下度量。
|
||||
|
||||
- ASL (平均句子长度) 由每个句子的单词数给出。打印 ASL。
|
||||
- PHW (困难词的百分比): 要计算这个值,首先计算三个或更多音节且不包含连字符 (-) 的单词数,以及不以 'es' 或 'ed' 结尾的三音节单词数。将这个计数除以文本中的总单词数,再乘以 100 得到百分比。打印 PHW。
|
||||
- 将 PHW 计算中使用的所有单词收集到一个列表中,与它们在输入中出现的方式完全一致,并打印这个列表。
|
||||
- ASYL (平均音节数) 由音节总数除以单词总数给出。打印 ASYL。
|
||||
- GFRI 由公式 0.4*(ASL + PHW) 给出。打印 GFRI。
|
||||
- FKRI 由公式 206.835-1.015*ASL-86.4*ASYL 给出。打印 FKRI。
|
||||
|
||||
请注意,GFRI 和 FKRI 这两个指标是著名的可读性指标 Gunning-Fog 和 Flesch Kincaid 的略微修改版本。在 Gunning-fog 中,计算出的值越高,文本就越难读。对于 Flesch Kincaid 来说则相反,值越高表示文本越容易阅读。
|
||||
|
||||
你可以在 `hw3_files.zip` 中的 `hw3_part1_01.txt` 和 `hw3_part1_02.txt` 中找到程序运行的示例。
|
||||
|
||||
完成后,请将你的程序作为 `hw3_part1.py` 提交到 Submitty。你必须使用这个文件名,否则你的提交在 Submitty 中将无法工作。你不必提交我们提供的任何文件。
|
||||
|
||||
## 第 2 部分: 野外的皮卡丘! (40 分)
|
||||
|
||||
假设你有一只皮卡丘站在图像的中间,坐标为 (75, 75)。假设图像左上角的坐标为 (0,0)。
|
||||
|
||||
我们将带着皮卡丘在图像中走动,寻找其他宝可梦。这是一种简单的模拟。首先,我们将通过询问用户模拟的回合数 (从第 0 回合开始)、皮卡丘的名字以及我们遇到其他宝可梦的频率来设置模拟参数。此时我们进入一个模拟循环 (while)。你的皮卡丘每回合走 5 步,方向为北 (N)、南 (S)、东 (E) 或西 (W)。每回合,询问用户皮卡丘行走的方向,并将皮卡丘移动到该方向。你应该忽略 N、S、E、W 以外的方向。每隔 often 回合,你就会遇到另一只宝可梦。询问用户类型 (地面 (G) 或水 (W))。如果是地面类型 'G',你的皮卡丘就输了。它转身并以看到另一只宝可梦之前移动方向的相反方向跑 10 步。 (如果最后的方向不是有效方向,你的皮卡丘就不移动。) 如果是水类型 'W',你的皮卡丘就赢了,并向前走 1 步。其他任何情况都意味着你实际上没有看到另一只宝可梦。在列表中记录胜利、失败和 "无宝可梦"。
|
||||
|
||||
在 turn 回合结束时,报告你的皮卡丘最终到达的位置,并打印出它的记录。
|
||||
|
||||
你必须为这个程序实现至少一个函数:
|
||||
|
||||
```python
|
||||
move_pokemon((row, column), direction, steps)
|
||||
```
|
||||
|
||||
它返回皮卡丘的下一个位置作为一个 (row, column) 元组。图像边界有一个围栏。没有坐标可以小于 0 或大于 150。0 和 150 是允许的。确保你的 `move_pokemon()` 函数不会返回这个范围之外的位置。
|
||||
|
||||
你可以使用以下代码来测试你的 `move_pokemon()` 函数。如果你愿意,可以随意编写其他函数,但一定要测试它们以确保它们按预期工作!
|
||||
|
||||
```python
|
||||
from hw3_part2 import move_pokemon
|
||||
|
||||
row = 15
|
||||
column = 10
|
||||
print(move_pokemon((row, column), 'n', 20)) # 应该打印 (0, 10)
|
||||
print(move_pokemon((row, column), 'e', 20)) # 应该打印 (15, 30)
|
||||
print(move_pokemon((row, column), 's', 20)) # 应该打印 (35, 10)
|
||||
print(move_pokemon((row, column), 'w', 20)) # 应该打印 (15, 0)
|
||||
|
||||
row = 135
|
||||
column = 140
|
||||
print(move_pokemon((row, column), 'N', 20)) # 应该打印 (115, 140)
|
||||
print(move_pokemon((row, column), 'E', 20)) # 应该打印 (135, 150)
|
||||
print(move_pokemon((row, column), 'S', 20)) # 应该打印 (150, 140)
|
||||
print(move_pokemon((row, column), 'W', 20)) # 应该打印 (135, 120)
|
||||
```
|
||||
|
||||
现在,编写一些代码,为输入的每个命令调用这些函数,并相应地更新皮卡丘的位置。
|
||||
|
||||
在文件 `hw3_part2_01.txt` 和 `hw3_part2_02.txt` (可以在 `hw03_files.zip` 文件中找到) 中提供了使用 Spyder IDE 运行程序时的两个示例 (它运行时的样子)。在 `hw3_part2_01.txt` 中,请注意 `f` 是无效方向,所以它对皮卡丘的状态没有影响,而 `r` 是无效的宝可梦类型,在结果列表中被标记为 "No Pokemon"。
|
||||
|
||||
我们将使用示例文件中的值以及其他一系列值来测试你的代码。好好测试你的代码,当你确定它可以工作时,请将其作为名为 `hw3_part2.py` 的文件提交到 Submitty 作为家庭作业的第 2 部分。
|
||||
|
||||
## 第 3 部分: 种群变化 - 有熊 (20 分)
|
||||
|
||||
你将编写一个程序来计算一种类似于你在实验 3 中计算的兔子和狐狸的种群平衡问题。这个问题将涉及熊、浆果田和游客。我们将只使用 "浆果" 这个词来表示浆果田的面积。我们也将计算熊和游客的数量。
|
||||
|
||||
熊需要大量的浆果来生存并为冬天做准备。因此,浆果田的面积对它们的种群非常重要。浆果田通常会随着时间的推移而扩散,但如果被熊过度践踏,它们可能会停止生长,面积可能会缩小。游客是熊最大的敌人,经常使它们习惯于人类,导致攻击性行为。可悲的是,这可能导致熊被杀死以避免对人类生命的威胁。
|
||||
|
||||
下面是每个群体从一年到下一年之间的种群联系方式。假设变量 `bears` 存储在给定年份的熊的数量, `berries` 存储浆果田的面积。
|
||||
|
||||
- 给定年份的游客数量按以下方式确定。如果熊的数量少于 4 只或多于 15 只,就没有游客。对他们来说,要么不够有趣,要么太危险。在其他情况下,每只熊最多有 10,000 名游客,每增加一只熊就有 20,000 名游客。为计算游客编写一个函数并单独测试它是一个好主意。
|
||||
- 根据给定年份的熊、浆果和游客的数量,下一年的熊和浆果的数量由以下公式决定:
|
||||
|
||||
```python
|
||||
bears_next = berries/(50*(bears+1)) + bears*0.60 - (math.log(1+tourists,10)*0.1)
|
||||
berries_next = (berries*1.5) - (bears+1)*(berries/14) - (math.log(1+tourists,10)*0.05)
|
||||
```
|
||||
|
||||
请记住,这些值都不能最终为负数。负值应该被限制为零。此外,熊和游客是整数。 `log` 函数在 `math` 模块中。
|
||||
|
||||
你必须编写一个函数,它接受给定年份的熊、浆果和游客的数量作为输入,并返回下一年的熊数量和浆果田面积作为元组。
|
||||
|
||||
```python
|
||||
>>> find_next(5, 1000, 40000)
|
||||
(5, 1071.1984678861438)
|
||||
```
|
||||
|
||||
然后编写主程序,读取两个值,即当前的熊数量和浆果田面积。你的程序然后找到并打印第一年和另外 9 年 (总共 10 年) 的所有三个群体 (熊、浆果和游客) 的数量。你必须使用循环来做到这一点。输出的格式是,所有值都以列的形式打印出来,并在每一列内左对齐。每一列的宽度正好是 10 个字符 (如果需要,用空格填充)。所有浮点值需要打印成正好一位小数。
|
||||
|
||||
完成后,你的程序应该输出: 在你的计算中达到的熊、浆果和游客数量的最小值和最大值。这些值应该使用与每年种群值相同的格式规则输出。
|
||||
|
||||
文件 `hw3_part3_01.txt` (可以在 `hw03_files.zip` 文件中找到) 提供了一个使用 Spyder IDE 运行程序时的示例 (它运行时的样子)。请注意,熊的数量可能会降到零,然后再回升。为什么? 邻近地区的熊可以迁移过来。熊、浆果和游客的最小值和最大值可能来自不同的年份。
|
||||
|
||||
我们将使用示例文件中的值以及其他一系列值来测试你的代码。好好测试你的代码,当你确定它可以工作时,请将其作为名为 `hw3_part3.py` 的文件提交到 Submitty 作为家庭作业的第 3 部分。
|
||||
|
||||
## 支持文件
|
||||
|
||||
{{< link href="HW3.zip" content="HW3.zip" title="下载 HW3.zip" download="HW3.zip" card=true >}}
|
||||
|
||||
## 参考答案
|
||||
|
||||
### hw3_part1.py
|
||||
|
||||
```python
|
||||
import syllables
|
||||
|
||||
#user_input = "We hold these truths to be self-evident, that all men are created equal, that they are endowed by their Creator with certain unalienable Rights, that among these are Life, Liberty and the pursuit of Happiness."
|
||||
#user_input = "There is a theory which states that if ever anyone discovers exactly what the Universe is for and why it is here, it will instantly disappear and be replaced by something even more bizarre and inexplicable. There is another theory which states that this has already happened."
|
||||
user_input = str(input("Enter a paragraph => ")).strip()
|
||||
print(user_input)
|
||||
|
||||
# Calculate ASL
|
||||
|
||||
sentences = user_input.split(". ")
|
||||
words_per_sentence = []
|
||||
#print(sentences)
|
||||
|
||||
for sentence in sentences:
|
||||
words = sentence.split()
|
||||
words_per_sentence.append(len(words))
|
||||
|
||||
asl = sum(words_per_sentence) / len(words_per_sentence)
|
||||
#print(asl)
|
||||
|
||||
# Calculate PHW
|
||||
|
||||
words = user_input.split()
|
||||
#print(len(words))
|
||||
|
||||
#find hard words
|
||||
hard_words = []
|
||||
|
||||
for word in words:
|
||||
num_syllables = syllables.find_num_syllables(word)
|
||||
#print(num_syllables)
|
||||
if num_syllables >= 3 and "-" not in word and word[-2:] != "es" and word[-2:] != "ed":
|
||||
#print(words)
|
||||
hard_words.append(word)
|
||||
#print(hard_words)
|
||||
|
||||
#print(hard_words)
|
||||
#print(len(hard_words))
|
||||
#print(words)
|
||||
#print(len(words))
|
||||
phw = len(hard_words) / len(words) * 100
|
||||
|
||||
# Calculate ASYL
|
||||
|
||||
total_syllables = 0
|
||||
|
||||
for word in words:
|
||||
total_syllables += syllables.find_num_syllables(word)
|
||||
|
||||
asyl = total_syllables / len(words)
|
||||
|
||||
# Caclulate GFRI
|
||||
|
||||
gfri = 0.4 * (asl + phw)
|
||||
|
||||
# Calculate FKRI
|
||||
|
||||
fkri = 206.835 - 1.015 * asl - 86.4 * asyl
|
||||
|
||||
print("Here are the hard words in this paragraph:\n" + str(hard_words))
|
||||
print("Statistics: ASL:{:.2f} PHW:{:.2f}% ASYL:{:.2f}".format(asl, phw, asyl))
|
||||
print("Readability index (GFRI): {:.2f}".format(gfri))
|
||||
print("Readability index (FKRI): {:.2f}".format(fkri))
|
||||
```
|
||||
|
||||
### hw3_part2.py
|
||||
|
||||
```python
|
||||
# Get user input
|
||||
|
||||
num_turn = int(input("How many turns? => ").strip())
|
||||
print(num_turn)
|
||||
pikachu_name = str(input("What is the name of your pikachu? => ").strip())
|
||||
print(pikachu_name)
|
||||
often_turn = int(input("How often do we see a Pokemon (turns)? => ").strip())
|
||||
print(str(often_turn) + "\n")
|
||||
|
||||
position = (75, 75)
|
||||
wins, losses, no_pokemon = 0, 0, 0
|
||||
turn_counter = 0
|
||||
records = []
|
||||
last_direction = ""
|
||||
|
||||
# Debugging
|
||||
|
||||
#num_turn = 5
|
||||
#print(num_turn)
|
||||
#pikachu_name = "Piki"
|
||||
#print(pikachu_name)
|
||||
#often_turn = 1
|
||||
#print(often_turn)
|
||||
|
||||
def move_pokemon(coords, direction, steps):
|
||||
global position
|
||||
x,y = coords
|
||||
direction = direction.lower()
|
||||
if direction in ['n', 's', 'e', 'w']:
|
||||
if direction == 'n':
|
||||
x = max(0, x - steps)
|
||||
elif direction == 'e':
|
||||
y = min(150, y + steps)
|
||||
elif direction == 's':
|
||||
x = min(150, x + steps)
|
||||
elif direction == 'w':
|
||||
y = max(0, y - steps)
|
||||
position = (x, y)
|
||||
|
||||
def walk(repeat):
|
||||
global turn_counter, last_direction
|
||||
for _ in range(repeat):
|
||||
direction = input("What direction does " + pikachu_name + " walk? => ").strip()
|
||||
print(direction)
|
||||
direction = direction.lower()
|
||||
if direction in ['n', 's', 'e', 'w']:
|
||||
move_pokemon(position, direction, 5)
|
||||
last_direction = direction
|
||||
turn_counter += 1
|
||||
|
||||
def turn_message(turn_counter, position):
|
||||
print("Turn " + str(turn_counter) + ", " + pikachu_name + " at " + str(position))
|
||||
|
||||
def battle(pokemon_type):
|
||||
global records, position, last_direction
|
||||
if pokemon_type.lower() == 'g':
|
||||
records.append('Lose')
|
||||
opposite_directions = {'n': 's', 's': 'n', 'e': 'w', 'w': 'e'}
|
||||
move_pokemon(position, opposite_directions[last_direction], 10)
|
||||
print(pikachu_name + " runs away to " + str(position))
|
||||
elif pokemon_type.lower() == 'w':
|
||||
records.append('Win')
|
||||
move_pokemon(position, last_direction, 1)
|
||||
print(pikachu_name + " wins and moves to " + str(position))
|
||||
else:
|
||||
records.append('No Pokemon')
|
||||
|
||||
print("Starting simulation, turn 0 " + pikachu_name + " at (75, 75)")
|
||||
|
||||
for i in range(num_turn // often_turn):
|
||||
walk(often_turn)
|
||||
turn_message(turn_counter, position)
|
||||
pokemon_type = input("What type of pokemon do you meet (W)ater, (G)round? => ").strip()
|
||||
print(pokemon_type)
|
||||
battle(pokemon_type)
|
||||
|
||||
if num_turn < often_turn:
|
||||
for i in range(num_turn):
|
||||
direction = input("What direction does " + pikachu_name + " walk? => ").strip()
|
||||
print(direction)
|
||||
direction = direction.lower()
|
||||
if direction in ['n', 's', 'e', 'w']:
|
||||
move_pokemon(position, direction, 5)
|
||||
last_direction = direction
|
||||
turn_counter += 1
|
||||
|
||||
print(pikachu_name + " ends up at " + "(" + str(position[0]) + ", " + str(position[1]) + ")" + ", Record: " + str(records))
|
||||
```
|
||||
|
||||
### hw3_part3.py
|
||||
|
||||
```python
|
||||
import math
|
||||
|
||||
n_bear = input("Number of bears => ").strip()
|
||||
print(n_bear)
|
||||
n_size = input("Size of berry area => ").strip()
|
||||
print(n_size)
|
||||
|
||||
n_bear = int(n_bear)
|
||||
n_size = float(n_size)
|
||||
|
||||
year = 10
|
||||
block_size = 10
|
||||
data = []
|
||||
title = ["Year", "Bears", "Berry", "Tourists"]
|
||||
|
||||
def print_line(b1, b2, b3, b4):
|
||||
b1 = b1 + " " * int(10 - len(b1))
|
||||
b2 = b2 + " " * int(10 - len(b2))
|
||||
b3 = b3 + " " * int(10 - len(b3))
|
||||
b4 = b4 + " " * int(10 - len(b4))
|
||||
print(b1 + b2 + b3 + b4)
|
||||
|
||||
def show_data(data):
|
||||
data_copy = data.copy()
|
||||
for line in data_copy:
|
||||
line[2] = "{:.1f}".format(float(line[2]))
|
||||
line = [str(i) for i in line]
|
||||
print_line(line[0], line[1], line[2], line[3])
|
||||
|
||||
def conclusion(data):
|
||||
#print(data)
|
||||
initial_line = data[0]
|
||||
max_bears, max_berry, max_tourists = initial_line[1], float(initial_line[2]), initial_line[3]
|
||||
min_bears, min_berry, min_tourists = initial_line[1], float(initial_line[2]), initial_line[3]
|
||||
for i in data:
|
||||
max_bears = max(max_bears, i[1])
|
||||
max_berry = max(max_berry, float(i[2]))
|
||||
max_tourists = max(max_tourists, i[3])
|
||||
min_bears = min(min_bears, i[1])
|
||||
min_berry = min(min_berry, float(i[2]))
|
||||
min_tourists = min(min_tourists, i[3])
|
||||
message = []
|
||||
message.append(["Min:", str(min_bears), str(min_berry), str(min_tourists)])
|
||||
message.append(["Max:", str(max_bears), str(max_berry), str(max_tourists)])
|
||||
show_data(message)
|
||||
|
||||
def find_next(bears, berry, tourists):
|
||||
bears_next = berry / (50*(bears+1)) + bears*0.60 - (math.log(1+tourists,10)*0.1)
|
||||
berries_next = (berry*1.5) - (bears+1)*(berry/14) - (math.log(1+tourists,10)*0.05)
|
||||
if berries_next < 0:
|
||||
berries_next = 0
|
||||
return (int(bears_next), berries_next)
|
||||
|
||||
def find_tourists(bears):
|
||||
bears = int(bears)
|
||||
if bears < 4 or bears > 15:
|
||||
tourists = 0
|
||||
elif bears <= 10:
|
||||
tourists = 10000*bears
|
||||
elif bears > 10:
|
||||
tourists = 100000 + 20000*(bears-10)
|
||||
return tourists
|
||||
|
||||
for i in range(year):
|
||||
tourists = find_tourists(n_bear)
|
||||
data.append([i+1, n_bear, n_size, tourists])
|
||||
n_bear, n_size = find_next(n_bear, n_size, tourists)
|
||||
|
||||
print_line(title[0], title[1], title[2], title[3])
|
||||
show_data(data)
|
||||
print()
|
||||
conclusion(data)
|
||||
```
|
||||
BIN
content/zh-cn/posts/csci-1100/hw-4/HW4.zip
Normal file
BIN
content/zh-cn/posts/csci-1100/hw-4/HW4.zip
Normal file
Binary file not shown.
389
content/zh-cn/posts/csci-1100/hw-4/index.md
Normal file
389
content/zh-cn/posts/csci-1100/hw-4/index.md
Normal file
@@ -0,0 +1,389 @@
|
||||
---
|
||||
title: CSCI 1100 - 作业 4 - 循环和列表;密码和隔离
|
||||
subtitle:
|
||||
date: 2024-03-13T15:15:44-04:00
|
||||
slug: csci-1100-hw-4
|
||||
draft: false
|
||||
author:
|
||||
name: James
|
||||
link: https://www.jamesflare.com
|
||||
email:
|
||||
avatar: /site-logo.avif
|
||||
description: 本文概述了 CSCI 1100 计算机科学导论课程第四次作业的要求和指南。作业包括两部分内容:使用 Python 编程评估密码强度,以及分析 COVID-19 疫情期间各州的隔离情况。
|
||||
keywords: ["CSCI 1100","计算机科学","Python","密码强度","COVID-19"]
|
||||
license:
|
||||
comment: true
|
||||
weight: 0
|
||||
tags:
|
||||
- CSCI 1100
|
||||
- 作业
|
||||
- RPI
|
||||
- Python
|
||||
- 编程
|
||||
categories:
|
||||
- 编程语言
|
||||
collections:
|
||||
- CSCI 1100
|
||||
hiddenFromHomePage: false
|
||||
hiddenFromSearch: false
|
||||
hiddenFromRss: false
|
||||
hiddenFromRelated: false
|
||||
summary: 本文概述了 CSCI 1100 计算机科学导论课程第四次作业的要求和指南。作业包括两部分内容:使用 Python 编程评估密码强度,以及分析 COVID-19 疫情期间各州的隔离情况。
|
||||
resources:
|
||||
- name: featured-image
|
||||
src: featured-image.jpg
|
||||
- name: featured-image-preview
|
||||
src: featured-image-preview.jpg
|
||||
toc: true
|
||||
math: true
|
||||
lightgallery: false
|
||||
password:
|
||||
message:
|
||||
repost:
|
||||
enable: true
|
||||
url:
|
||||
|
||||
# 查看详细的 front matter: https://fixit.lruihao.cn/documentation/content-management/introduction/#front-matter
|
||||
---
|
||||
|
||||
<!--more-->
|
||||
|
||||
## 作业概述
|
||||
|
||||
本次作业总分为 100 分,将计入你的总作业成绩。截止日期为一周后,即 2024 年 2 月 22 日(星期四)晚上 11:59:59。和往常一样,作业包括自动评分、教师测试用例评分和助教评分三部分。作业分为两个部分,需分别提交。所有部分都必须在截止日期前提交,否则你的程序将被视为逾期。
|
||||
|
||||
关于评分标准和过度合作的讨论,请参阅提交指南和合作政策手册。这些规则将在本学期剩余时间内持续有效。
|
||||
|
||||
你需要使用我们在 `hw4_files.zip` 中提供的实用工具和数据文件,所以请务必从 Submitty 的课程资料部分下载该文件,并将其解压缩到你的作业 4 目录中。
|
||||
|
||||
`hw4_util.py` 模块旨在帮助你从文件中读取信息。你不需要了解 `hw4_util.py` 中提供的函数是如何实现的(但如果你感兴趣,可以随意查看代码),只需直接使用它们即可。
|
||||
|
||||
最后需要注意的是,你需要在本次作业中使用循环。我们将根据任务和你的个人偏好,让你自行选择使用 while 循环还是 for 循环。
|
||||
|
||||
## 第一部分:密码强度
|
||||
|
||||
创建密码时,通常会评估其强度。强度估计是通过应用几个规则来计算的,包括密码长度、特定类型字符的存在、与常见密码的匹配程度,甚至与车牌号的匹配程度。在作业的这一部分,你将实现一些简单的强度判断规则,然后确定密码是被拒绝,还是被评为差、一般、好或优秀。
|
||||
|
||||
你的程序应该先要求用户输入一个密码并读取它。然后根据以下规则评估密码强度。每个规则都会对一个数值分数(初始为 0)产生影响:
|
||||
|
||||
1. **长度**:如果密码长度为 6 或 7 个字符,则加 1 分;如果长度为 8、9 或 10 个字符,则加 2 分;如果长度超过 10 个字符,则加 3 分。
|
||||
2. **大小写**:如果密码包含至少两个大写字母和两个小写字母,则加 2 分;如果至少包含一个大写字母和一个小写字母,则加 1 分。
|
||||
3. **数字**:如果密码包含至少两个数字,则加 2 分;如果至少包含一个数字,则加 1 分。
|
||||
4. **标点符号**:如果密码包含至少一个 !@#$ 符号,则加 1 分;如果至少包含一个 %^&* 符号,则再加 1 分(总共最多加 2 分)。
|
||||
5. **纽约车牌**:如果密码包含三个字母(大写或小写)后跟四个数字,则可能与纽约州车牌号格式匹配。在这种情况下,扣 2 分。
|
||||
6. **常见密码**:如果密码的小写形式与常见密码列表中的某个密码完全匹配,则扣 3 分。
|
||||
|
||||
每当应用一条规则导致分数发生变化时,生成一行解释性输出。在应用所有规则后,输出最终得分,然后将其转换为密码的强度等级:
|
||||
|
||||
- **拒绝**:分数小于或等于 0。
|
||||
- **差**:分数为 1 或 2。
|
||||
- **一般**:分数为 3 或 4。
|
||||
- **好**:分数为 5 或 6。
|
||||
- **优秀**:分数为 7 或以上。
|
||||
|
||||
### 注意事项
|
||||
|
||||
1. 对于第一部分和第二部分,你应该编写函数以保持代码的整洁、清晰和易于管理。
|
||||
2. 我们为你提供了许多示例,展示了输出值和格式。请仔细遵循这些示例。
|
||||
3. 常见密码是从文件中提取的。我们提供的实用函数之一会读取该文件并返回这些密码的列表。要使用此函数,首先确保 `hw4_util.py` 和 `password_list_top_100.txt` 与你的代码在同一文件夹中。然后在你的程序中添加以下行:
|
||||
```python
|
||||
import hw4_util
|
||||
```
|
||||
最后,调用不带任何参数的函数 `hw4_util.part1_get_top()`。它将返回一个包含 100 个密码的字符串列表供你进行比较。
|
||||
4. 只提交你的程序文件 `hw4_part1.py`。不要提交 `hw4_util.py`。
|
||||
|
||||
## 第二部分:COVID-19 隔离州
|
||||
|
||||
纽约州 COVID-19 旅行建议 [COVID-19 Travel Advisory](https://coronavirus.health.ny.gov/covid-19-travel-advisory) 要求从 COVID-19 社区传播严重的州来到纽约的个人必须自我隔离 14 天。一个州的"严重传播"是指:
|
||||
|
||||
- 在过去七天中,平均每天每 10 万居民中有超过 10 人检测呈阳性,或者
|
||||
- 在过去七天中,平均每天超过 10% 的检测呈阳性。
|
||||
|
||||
我们将具有严重传播的州称为隔离州。在作业的这一部分中,你将使用从 [COVID Tracking Project](https://covidtracking.com/) 下载的各州数据来回答有关哪些州是隔离州以及何时是隔离州的查询。
|
||||
|
||||
我们获得的数据是在 2023 年 10 月 5 日以大型"逗号分隔值"文件的形式下载的。该文件每个州每天包含一行,每行有许多字段。我们已将其浓缩为适合 CS1 作业的形式。这些数据是根据知识共享 BY 4.0 许可证共享的,这意味着我们可以:
|
||||
|
||||
- 共享:以任何媒体或格式复制和重新分发材料,以及
|
||||
- 改编:以任何目的重新混合、转换和构建材料,甚至是商业目的。
|
||||
|
||||
我们提供了一个简单的实用程序,让你可以访问浓缩后的数据。要使用它(与第一部分类似),你必须在与你自己的代码相同的文件夹中拥有 `hw4_util.py` 和 `prob2_data.csv` 文件。然后你必须在程序中导入 `hw4_util` 模块:
|
||||
|
||||
```python
|
||||
import hw4_util
|
||||
```
|
||||
|
||||
`hw4_util` 有一个名为 `part2_get_week` 的函数,它接受一个整数参数 `w`,并返回一个列表的列表。参数 `w` 是前几周的索引,其中 `w==1` 表示最近一周,`w==2` 表示前一周,以此类推,直到 `w==29`,对应于 29 周前,一直追溯到 3 月 15 日。返回的列表包含每个州一个子列表,加上哥伦比亚特区 (DC) 和波多黎各 (PR),总共 52 个。每个州的子列表有 16 个元素:
|
||||
|
||||
- 元素 0 是一个字符串,表示两个字母(大写)的州缩写。这些缩写是正确的。
|
||||
- 元素 1 是一个整数,表示该州的人口估计值,来自 2019 年人口普查局估计 [Census Bureau estimate](https://www.census.gov/newsroom/press-kits/2019/national-state-estimates.html)。
|
||||
- 元素 2-8 是指定周的七天中每一天该州的阳性检测数,最近的日期在前。
|
||||
- 元素 9-15 是指定周的七天中每一天该州的阴性检测数,最近的日期在前。
|
||||
|
||||
例如,第 1 周阿拉斯加州的子列表是:
|
||||
|
||||
```text
|
||||
['AK',\
|
||||
731545,\
|
||||
189,147,128,132,106,125,118,\
|
||||
3373,3819,6839,4984,6045,6140,1688]
|
||||
```
|
||||
|
||||
以下是你在这个作业中需要完成的任务。你的程序应该在一个循环中,首先要求用户指定一个周的索引,如上所述。(你可以假设输入一个整数作为周数。)负数表示程序应该结束。对于非负数,如果该周的数据不可用,函数将返回一个空列表;在这种情况下,跳过循环体的其余部分。否则,在获得列表的列表后,程序应该回答关于该周的四个不同信息请求之一。回答请求首先由用户输入一个关键字。关键字包括 'daily'、'pct'、'quar'、'high'。对于每个请求,程序必须执行以下操作:
|
||||
|
||||
- **'daily'**:询问用户州缩写,然后输出该州在给定周内平均每日每 10 万人中的阳性病例数,精确到小数点后一位。
|
||||
- **'pct'**:询问用户州缩写,然后输出该州在给定周内平均每日检测阳性百分比,精确到百分之一。
|
||||
- **'quar'**:按两个字母缩写的字母顺序输出给定周的旅行隔离州的州缩写列表,如上所述。每行应有十个州缩写,使用你的缩写列表调用 `hw4_util.print_abbreviations` 函数以按要求打印输出。(注意:每周至少有一个隔离州。)
|
||||
- **'high'**:输出在给定周内平均每日每 10 万人中阳性病例数最高的州的两个字母缩写,并输出这个平均数,精确到小数点后一位。
|
||||
|
||||
输入的关键字和州缩写可以使用大写或小写字母,程序仍然应该能够正确匹配。如果关键字不是这四个之一,或者由于州缩写输入错误而未找到对应的州,则输出一个简单的错误消息,并在当前循环迭代中不执行其他操作。
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 和往常一样,查看我们提供的示例输出并准确遵循它。
|
||||
2. 所有报告的阳性和阴性检测结果数量至少为 0,但有些可能为 0。不过,你可以假设不会出现一周内所有日子的阴性检测数都为 0 的情况。
|
||||
3. 通过对一周的阳性病例求和以及对一周的阴性病例求和来计算每日检测阳性的百分比。如果这些和分别为 P 和 N,则阳性百分比为 $P/(P+N) * 100$。这与一周内每日百分比的平均值不完全相同,但更容易计算。
|
||||
4. 只提交你的程序文件 `hw4_part2.py`。不要提交 `hw4_util.py`。
|
||||
|
||||
## 支持文件
|
||||
|
||||
{{< link href="HW4.zip" content="HW4.zip" title="下载 HW4.zip" download="HW4.zip" card=true >}}
|
||||
|
||||
## 参考答案
|
||||
|
||||
### hw4_part1.py
|
||||
|
||||
```python
|
||||
"""
|
||||
This script is used to test password strength based on certain criteria.
|
||||
Author: Jinshan Zhou
|
||||
"""
|
||||
|
||||
import hw4_util
|
||||
|
||||
if __name__ == "__main__":
|
||||
# initialize variables
|
||||
strength = 0
|
||||
report = ""
|
||||
|
||||
# Debugging
|
||||
|
||||
#user_password = "AdmIn123%^%*(&"
|
||||
#user_password = "jaX1234"
|
||||
|
||||
# get user input
|
||||
user_password = str(input("Enter a password => ").strip())
|
||||
|
||||
# print the password for testing purposes
|
||||
print(user_password)
|
||||
|
||||
# get the length of the password
|
||||
length = len(user_password)
|
||||
|
||||
# check the length of the password and update strength and report accordingly
|
||||
if length <= 7 and length >= 6:
|
||||
strength += 1
|
||||
report += "Length: +1\n"
|
||||
elif length >= 8 and length <= 10:
|
||||
strength += 2
|
||||
report += "Length: +2\n"
|
||||
elif length > 10:
|
||||
strength += 3
|
||||
report += "Length: +3\n"
|
||||
|
||||
# count the number of uppercase and lowercase letters in the password
|
||||
num_upper = sum(1 for c in user_password if c.isupper())
|
||||
num_lower = sum(1 for c in user_password if c.islower())
|
||||
|
||||
# check the number of uppercase and lowercase letters and update strength and report accordingly
|
||||
if num_upper >= 2 and num_lower >= 2:
|
||||
strength += 2
|
||||
report += "Cases: +2\n"
|
||||
elif num_upper >= 1 and num_lower >= 1:
|
||||
strength += 1
|
||||
report += "Cases: +1\n"
|
||||
|
||||
# count the number of digits in the password
|
||||
num_digits = sum(1 for c in user_password if c.isdigit())
|
||||
|
||||
# check the number of digits and update strength and report accordingly
|
||||
if num_digits >= 2:
|
||||
strength += 2
|
||||
report += "Digits: +2\n"
|
||||
elif num_digits >= 1:
|
||||
strength += 1
|
||||
report += "Digits: +1\n"
|
||||
|
||||
# check for special characters and update strength and report accordingly
|
||||
if any(c in "!@#$" for c in user_password):
|
||||
strength += 1
|
||||
report += "!@#$: +1\n"
|
||||
if any(c in "%^&*" for c in user_password):
|
||||
strength += 1
|
||||
report += "%^&*: +1\n"
|
||||
|
||||
# check for a specific pattern and update strength and report accordingly
|
||||
if (num_upper + num_lower) == 3 and num_digits == 4 and len(user_password) > 3:
|
||||
check = user_password.replace(user_password[0:3], "")
|
||||
if sum(1 for c in check if c.isdigit()) == 4:
|
||||
strength -= 2
|
||||
report += "License: -2\n"
|
||||
|
||||
# check if the password is in the top 10,000 most common passwords and update strength and report accordingly
|
||||
if user_password.lower() in hw4_util.part1_get_top():
|
||||
strength -= 3
|
||||
report += "Common: -3\n"
|
||||
|
||||
# add the combined score to the report
|
||||
report += "Combined score: " + str(strength) + "\n"
|
||||
|
||||
# check the strength and add the appropriate message to the report
|
||||
if strength <= 0:
|
||||
report += "Password is rejected"
|
||||
elif strength >= 1 and strength <= 2:
|
||||
report += "Password is poor"
|
||||
elif strength >= 3 and strength <= 4:
|
||||
report += "Password is fair"
|
||||
elif strength >= 5 and strength <= 6:
|
||||
report += "Password is good"
|
||||
elif strength >= 7:
|
||||
report += "Password is excellent"
|
||||
|
||||
# print the report
|
||||
print(report)
|
||||
```
|
||||
|
||||
### hw4_part2.py
|
||||
|
||||
```python
|
||||
import hw4_util
|
||||
|
||||
"""
|
||||
hw4_util.part2_get_week(1)[0] ==> ['AK',\
|
||||
731545, 189, 147, 128, 132, 106, 125,\
|
||||
118, 3373, 3819, 6839, 4984, 6045,\
|
||||
6140, 1688]
|
||||
"""
|
||||
|
||||
def find_state(states, state):
|
||||
state = state.upper()
|
||||
found_status = False
|
||||
for i in states:
|
||||
if i[0].upper() == state:
|
||||
found_status = True
|
||||
return i
|
||||
if not found_status:
|
||||
return []
|
||||
|
||||
def get_postive_per_100k(status):
|
||||
population = status[1]
|
||||
total_postive = 0
|
||||
for i in range (2, 9):
|
||||
total_postive += status[i]
|
||||
postive_per_100k = ((total_postive / 7) / population) * 100000
|
||||
return postive_per_100k
|
||||
|
||||
def get_pct_postive_tests(status):
|
||||
num_tested = 0
|
||||
num_postive = 0
|
||||
for i in range (2, 16):
|
||||
num_tested += status[i]
|
||||
for i in range (2, 9):
|
||||
num_postive += status[i]
|
||||
pct_postive_tests = num_postive / num_tested
|
||||
return pct_postive_tests
|
||||
|
||||
def action_valid(request_code):
|
||||
request_code = request_code.lower()
|
||||
allowed_action = ["daily", "pct", "quar", "high"]
|
||||
if request_code in allowed_action:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def quar(week):
|
||||
states = []
|
||||
for i in week:
|
||||
if get_postive_per_100k(i) >= 10 or get_pct_postive_tests(i) >= 0.1:
|
||||
states.append(i[0])
|
||||
states.sort()
|
||||
return states
|
||||
|
||||
def high(week):
|
||||
highest = ""
|
||||
highest_value = 0
|
||||
for i in week:
|
||||
if get_postive_per_100k(i) > highest_value:
|
||||
highest = i[0]
|
||||
highest_value = get_postive_per_100k(i)
|
||||
return highest.upper()
|
||||
|
||||
def show_high(week):
|
||||
highest = high(week)
|
||||
highest_postive_per_100k = get_postive_per_100k(find_state(week, highest))
|
||||
print("State with highest infection rate is", highest)
|
||||
print("Rate is {:.1f} per 100,000 people".format(highest_postive_per_100k))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
index_week = 0 # Initialize index_week
|
||||
print("...")
|
||||
|
||||
while index_week != -1:
|
||||
# Get index of week
|
||||
index_week = input("Please enter the index for a week: ").strip()
|
||||
print(index_week)
|
||||
index_week = int(index_week)
|
||||
|
||||
# Stop if the index is -1
|
||||
if index_week < 0:
|
||||
break
|
||||
|
||||
# Get the week, check if the week is valid
|
||||
week = hw4_util.part2_get_week(index_week).copy()
|
||||
if week == []:
|
||||
print("No data for that week")
|
||||
print("...")
|
||||
continue
|
||||
|
||||
# Get the Action
|
||||
request_code = input("Request (daily, pct, quar, high): ").strip()
|
||||
print(request_code)
|
||||
request_code = request_code.lower()
|
||||
|
||||
# Check if the action is valid
|
||||
if not action_valid(request_code):
|
||||
print("Unrecognized request")
|
||||
print("...")
|
||||
continue
|
||||
|
||||
# Perform the action
|
||||
if request_code == "daily":
|
||||
state = input("Enter the state: ").strip()
|
||||
print(state)
|
||||
state = state.upper()
|
||||
if find_state(week,state) == []:
|
||||
print("State {} not found".format(state))
|
||||
print("...")
|
||||
else:
|
||||
state_data = find_state(week,state)
|
||||
print("Average daily positives per 100K population: {:.1f}".format(get_postive_per_100k(state_data)))
|
||||
print("...")
|
||||
elif request_code == "pct":
|
||||
state = input("Enter the state: ").strip()
|
||||
print(state)
|
||||
state = state.upper()
|
||||
if find_state(week,state) == []:
|
||||
print("State {} not found".format(state))
|
||||
print("...")
|
||||
else:
|
||||
state_data = find_state(week,state)
|
||||
print("Average daily positive percent: {:.1f}".format(get_pct_postive_tests(state_data)*100))
|
||||
print("...")
|
||||
elif request_code == "quar":
|
||||
print("Quarantine states:")
|
||||
hw4_util.print_abbreviations(quar(week))
|
||||
print("...")
|
||||
elif request_code == "high":
|
||||
show_high(week)
|
||||
print("...")
|
||||
```
|
||||
BIN
content/zh-cn/posts/csci-1100/hw-5/HW5.zip
Normal file
BIN
content/zh-cn/posts/csci-1100/hw-5/HW5.zip
Normal file
Binary file not shown.
473
content/zh-cn/posts/csci-1100/hw-5/index.md
Normal file
473
content/zh-cn/posts/csci-1100/hw-5/index.md
Normal file
@@ -0,0 +1,473 @@
|
||||
---
|
||||
title: CSCI 1100 - 作业5 - 嵌套列表、网格和路径规划
|
||||
subtitle:
|
||||
date: 2024-03-13T15:36:47-04:00
|
||||
slug: csci-1100-hw-5
|
||||
draft: false
|
||||
author:
|
||||
name: James
|
||||
link: https://www.jamesflare.com
|
||||
email:
|
||||
avatar: /site-logo.avif
|
||||
description: 本文概述了 CSCI 1100 计算机科学导论课程中一项关于使用 Python 进行嵌套列表、网格和路径规划的编程作业。文章包括每个部分的问题描述、指导方针和示例输出。
|
||||
keywords: ["Python","计算机科学","列表","网格","路径规划"]
|
||||
license:
|
||||
comment: true
|
||||
weight: 0
|
||||
tags:
|
||||
- CSCI 1100
|
||||
- 作业
|
||||
- RPI
|
||||
- Python
|
||||
- 编程
|
||||
categories:
|
||||
- 编程语言
|
||||
collections:
|
||||
- CSCI 1100
|
||||
hiddenFromHomePage: false
|
||||
hiddenFromSearch: false
|
||||
hiddenFromRss: false
|
||||
hiddenFromRelated: false
|
||||
summary: 本文概述了 CSCI 1100 计算机科学导论课程中一项关于使用 Python 进行嵌套列表、网格和路径规划的编程作业。文章包括每个部分的问题描述、指导方针和示例输出。
|
||||
resources:
|
||||
- name: featured-image
|
||||
src: featured-image.jpg
|
||||
- name: featured-image-preview
|
||||
src: featured-image-preview.jpg
|
||||
toc: true
|
||||
math: false
|
||||
lightgallery: false
|
||||
password:
|
||||
message:
|
||||
repost:
|
||||
enable: true
|
||||
url:
|
||||
|
||||
# 有关详细信息,请参阅前言:https://fixit.lruihao.cn/documentation/content-management/introduction/#front-matter
|
||||
---
|
||||
|
||||
<!--more-->
|
||||
|
||||
## 作业概述
|
||||
|
||||
本次作业总分值 100 分,将计入你的总作业成绩。截止日期为 2024 年 2 月 29 日星期四晚上 11:59:59。和往常一样,作业评分由自动评分、讲师测试用例和助教评分三部分组成。作业分为两个部分,需分别提交。两部分都应在截止日期前提交,否则将被视为逾期。
|
||||
|
||||
关于评分标准和过度合作的定义,请参阅提交指南和合作政策。这些规则将在本学期剩余时间内执行。
|
||||
|
||||
你将需要我们在 `hw5_files.zip` 中提供的数据文件,请务必从 Submitty 的课程资料部分下载并解压到你的作业 5 目录。压缩包内包含实用代码、数据文件以及程序的示例输入/输出。
|
||||
|
||||
## 问题描述
|
||||
|
||||
计算机科学和工程领域的许多问题都是在二维数值网格上求解的,常用技术包括"梯度上升(或下降)"、贪心搜索或爬山算法。我们将使用手工生成的高程数据来研究其简化版本。
|
||||
|
||||
我们主要使用的表示方法是"高度"(也称"海拔",这里简称"高度")的嵌套列表。例如:
|
||||
|
||||
```python
|
||||
grid = [[15, 16, 18, 19, 12, 11],
|
||||
[13, 19, 23, 21, 16, 12],
|
||||
[12, 15, 17, 19, 22, 10],
|
||||
[10, 14, 16, 13, 9, 6]]
|
||||
```
|
||||
|
||||
这个网格包含四个长度为六的整数列表。每个元素表示一个高度值,如海拔高度,以固定间隔测量,间隔可小至厘米,也可大至公里。美国地质调查局(USGS)和一些私营公司都在维护和分发此类高程数据。这些数据在评估径流、确定风力涡轮机位置以及规划道路和建设等方面都很重要。我们将以规划徒步路线为例来研究这个问题。
|
||||
|
||||
对于这些数据,我们可能会问以下问题:
|
||||
|
||||
1. 最高点(最大高度)在哪里?这也称为"全局最大值",因为它是数据中的最大值。在上例中,最大高度为 23,出现在第 1 行第 2 列(简写为 (1, 2),假设第一个值表示行,第二个值表示列)。我们称 (1, 2) 为一个"位置"。
|
||||
2. 数据中是否存在"局部最大值"?即高度大于周围值但小于全局最大值的点。在上例中,位置 (2, 4) 处的 22 就是一个局部最大值。
|
||||
3. 从给定位置出发,到达全局最大值的最佳路径是什么?这是一个棘手的问题,因为我们需要定义"最佳路径"。一个简单的定义是:能否从给定位置出发,只走最陡的路线到达全局最大值(是否能徒步到"山顶")?例如,从 (3, 0) 出发,经过 (3, 0)、(3, 1)、(3, 2)、(2, 2)、(1, 2) 的路径是最陡的,可以到达顶峰。这是一种"梯度上升"方法。但如果从 (3, 5) 出发,会生成路径 (3, 5)、(3, 4)、(2, 4),之后就无法继续向上到达全局最大值。
|
||||
|
||||
我们还可以提出并回答更多问题。有些问题很容易解决,有些则需要复杂的算法和大量计算。
|
||||
|
||||
在开始实际作业之前,我们需要定义网格中"相邻"位置的概念,即从给定位置允许走到的位置。在本作业中,从位置 (r, c) 出发,相邻位置包括 (r-1, c)、(r, c-1)、(r, c+1) 和 (r+1, c)。
|
||||
|
||||
换句话说,相邻位置必须与当前位置在同一行或同一列。此外,相邻位置不能超出网格边界。例如,对于位置 (r, 0),只有 (r-1, 0)、(r, 1)、(r+1, 0) 是允许的相邻位置。
|
||||
|
||||
## 入门指南
|
||||
|
||||
请下载 `hw5_files.zip` 并将所有文件放在你要编写解决方案的同一目录下。`hw5_util.py` 和 `hw5_grids.txt` 这两个文件非常重要:`hw5_util.py` 包含从 `hw5_grids.txt` 读取网格和起始位置的实用函数。具体来说:
|
||||
|
||||
- `hw5_util.num_grids()` 返回数据中不同网格的数量。
|
||||
- `hw5_util.get_grid(n)` 返回第 n 个网格,其中 n==1 表示第一个网格,n == `hw5_util.num_grids()` 表示最后一个网格。
|
||||
- `hw5_util.get_start_locations(n)` 返回一个元组列表,给出第 n 个网格要考虑的一个或多个起始位置。
|
||||
- `hw5_util.get_path(n)` 返回一个元组列表,给出第 n 个网格的一条可能路径。
|
||||
|
||||
建议你先试用这些函数并打印结果,以确保你理解它们的功能。
|
||||
|
||||
你可以对数据做如下假设:
|
||||
|
||||
1. 网格至少有两行。
|
||||
2. 每行至少有两个元素(列),且每行的列数相同。
|
||||
3. 所有高度值都是正整数。
|
||||
4. 起始位置都在网格的行列范围内。
|
||||
5. 路径上的位置都在网格的行列范围内。
|
||||
|
||||
## 第一部分
|
||||
|
||||
编写一个 Python 程序 `hw5_part1.py`,完成以下任务:
|
||||
|
||||
1. 询问用户要使用的网格编号,并循环询问直到得到正确范围内的编号。将网格编号记为 n。
|
||||
2. 获取第 n 个网格。
|
||||
3. 询问用户是否要打印网格。如果用户输入单个字符 'Y' 或 'y',则打印网格,否则不打印。打印时可假设高度值小于 1000 米。参考示例输出。
|
||||
4. 获取与第 n 个网格关联的起始位置,对于每个起始位置,打印其在网格边界内的相邻位置。例如,如果第 n 个网格有 8 行 10 列,起始位置列表为 `[(4, 6), (0, 3), (7, 9)]`,则输出应为:
|
||||
|
||||
```plaintext
|
||||
Neighbors of (4, 6): (3, 6) (4, 5) (4, 7) (5, 6)
|
||||
Neighbors of (0, 3): (0, 2) (0, 4) (1, 3)
|
||||
Neighbors of (7, 9): (6, 9) (7, 8)
|
||||
```
|
||||
|
||||
非常重要:我们强烈建议你编写一个名为 `get_nbrs` 的函数,它接受行、列位置以及网格的行数和列数作为参数,返回一个元组列表,包含给定位置在网格边界内的所有相邻位置。你会经常用到这个函数。
|
||||
|
||||
5. 获取建议路径,判断它是否有效(每个位置都与下一个位置相邻),然后计算总下降高度和总上升高度。例如,对于上面的网格,如果路径为 `(3, 1), (3, 0), (2, 0), (1, 0), (1, 1), (0, 1), (0, 2), (1, 2)`,则下降高度为从 (3, 1) 到 (3, 0)(变化 4)和从 (1, 1) 到 (0, 1)(变化 3),总计 7;上升高度为从 (3, 0) 到 (2, 0)、从 (2, 0) 到 (1, 0)、从 (1, 0) 到 (1, 1)、从 (0, 1) 到 (0, 2) 以及从 (0, 2) 到 (1, 2),总计 (2 + 1 + 6 + 2 + 5) = 16。输出应为:
|
||||
|
||||
```plaintext
|
||||
有效路径
|
||||
下降 7
|
||||
上升 16
|
||||
```
|
||||
|
||||
如果路径无效,代码应输出 `路径:从 point1 到 point2 的无效步骤。` 其中 point1 和 point2 是表示无效步骤起点和终点的元组。
|
||||
|
||||
只提交文件 `hw5_part1.py`,不要提交其他任何内容。
|
||||
|
||||
## 第二部分
|
||||
|
||||
修改第一部分的解决方案,并将其提交为 `hw5_part2.py`。程序应再次要求用户输入网格编号,但不应打印网格。接下来,它应找到并输出全局最大高度的位置和高度值。例如,对于上面的简单网格,输出应为 `全局最大值:(1, 2) 23`。你可以不加验证地假设全局最大值是唯一的。
|
||||
|
||||
第二部分的主要任务是为网格的每个起始位置找到并输出两条路径。第一条是最陡峭的上升路径,第二条是最平缓的上升路径。每条路径上的步骤必须在相邻位置之间,与第一部分相同。此外,在每条路径上,不允许走到高度相同或更低的位置,并且步长(新位置与当前位置的高度差)不能超过最大步高(由用户输入)。
|
||||
|
||||
接下来,确定每条路径是到达了网格中全局最大高度的位置、局部最大值还是都不是。如果当前位置的唯一上升步骤相对于当前高度太高,就可能出现后一种情况。当然,真正的徒步路径可以上下起伏,但在这种更复杂的情况下寻找"最优路径"需要我们尚未准备好开发的更复杂算法。
|
||||
|
||||
以上面的网格为例:
|
||||
|
||||
```python
|
||||
grid = [[15, 16, 18, 19, 12, 11],
|
||||
[13, 19, 23, 21, 16, 12],
|
||||
[12, 15, 17, 19, 20, 10],
|
||||
[10, 14, 16, 13, 9, 6]]
|
||||
```
|
||||
|
||||
从位置 (3, 0) 出发,最大步高为 4,最陡峭的路径是 (3, 0)、(3, 1)、(3, 2)、(2, 2)、(2, 3)、(1, 3)、(1, 2),最平缓的路径是 (3, 0)、(2, 0)、(1, 0)、(0, 0)、(0, 1)、(0, 2)、(0, 3)、(1, 3)、(1, 2)。两条路径都到达了全局最大值,并且在第一次接近时都避免了直接走到全局最大值,因为步高太大。注意,从位置 (3, 5) 出发的最陡峭和最平缓路径都将在局部最大值 (2, 4) 处结束。最陡峭的路径将在 4 步后结束(路径上有 5 个位置),最平缓的路径将在 6 步后结束(路径上有 7 个位置)。如果最大步高只有 3,那么从 (3, 5) 出发的两条路径都将在到达任何最大值之前的位置 (3, 4) 处停止。
|
||||
|
||||
路径输出时每行 5 个位置,例如:
|
||||
|
||||
```plaintext
|
||||
steepest path
|
||||
(3, 0) (2, 0) (1, 0) (0, 0) (0, 1)
|
||||
(0, 2) (0, 3) (1, 3) (1, 2)
|
||||
global maximum
|
||||
```
|
||||
|
||||
更多细节请参考示例输出。
|
||||
|
||||
最后,如果用户请求,输出一个"路径网格",在每个位置给出包含该位置的路径数量。这可以通过创建一个新的嵌套列表来处理,其中每个元素表示一个计数,初始化为 0。对于每条路径和路径上的每个位置 (i, j),应增加嵌套列表中相应的计数。最后,在生成所有路径并将其添加到计数后,输出网格。在此输出中,对于不在任何路径上的位置,不要输出 0,而是输出 '.',这将使输出更清晰。请参阅示例输出。
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 在选择路径的下一个位置时,如果有多个选择,则选择 `get_nbrs` 函数生成的列表中较早出现的位置。例如,在上面的示例网格中,从高度为 11 的位置 (0, 5) 出发,(0, 4) 和 (1, 5) 的高度都是 12。在这种情况下,(0, 4) 将在 `get_nbrs` 列表中较早出现,因此被选为路径上的下一个位置。
|
||||
2. 在确保其他所有内容都正常工作之前,请不要处理路径网格输出(最后一步)。
|
||||
3. 最平缓和最陡峭的路径都是贪心算法的示例,其中在每一步都做出当前最优选择,而不重新考虑之前的决定。更复杂的算法会考虑某种形式的回溯,即撤销决策并重新考虑替代方案。
|
||||
|
||||
## 支持文件
|
||||
|
||||
{{< link href="HW5.zip" content="HW5.zip" title="下载 HW5.zip" download="HW5.zip" card=true >}}
|
||||
|
||||
## 参考答案
|
||||
|
||||
### hw5_part1.py
|
||||
|
||||
```python
|
||||
import hw5_util
|
||||
|
||||
def show_grid(grid):
|
||||
""" Display the grid in a human-readable format. """
|
||||
display = ""
|
||||
for row in grid:
|
||||
for cell in row:
|
||||
display += " {:2d}".format(int(cell))
|
||||
display += "\n"
|
||||
print(display, end="")
|
||||
|
||||
def find_grid_size(grid):
|
||||
""" Find the size of the grid. """
|
||||
return (len(grid), len(grid[0]))
|
||||
|
||||
def find_neighbors(point, grid):
|
||||
""" Find the neighbors of a point in the grid. """
|
||||
neighbors = []
|
||||
y, x = point
|
||||
max_y, max_x = find_grid_size(grid)
|
||||
neighbors.append((y-1, x)) if y-1 >= 0 else None
|
||||
neighbors.append((y, x-1)) if x-1 >= 0 else None
|
||||
neighbors.append((y, x+1)) if x+1 < max_x else None
|
||||
neighbors.append((y+1, x)) if y+1 < max_y else None
|
||||
neighbors.append((y-1, x-1)) if y-1 >= 0 and x-1 >= 0 else None
|
||||
neighbors.append((y-1, x+1)) if y-1 >= 0 and x+1 < max_x else None
|
||||
neighbors.append((y+1, x-1)) if y+1 < max_y and x-1 >= 0 else None
|
||||
neighbors.append((y+1, x+1)) if y+1 < max_y and x+1 < max_x else None
|
||||
return neighbors
|
||||
|
||||
def show_neighbors(start_locations, grid):
|
||||
""" Display the neighbors of the start locations."""
|
||||
for i in start_locations:
|
||||
neighbors = ""
|
||||
for j in find_neighbors(i, grid):
|
||||
neighbors += str(j) + " "
|
||||
neighbors = neighbors.strip()
|
||||
print("Neighbors of {}: {}".format(i, neighbors))
|
||||
|
||||
def path_validator(path, grid):
|
||||
""" Validate the path. """
|
||||
result = (True, "Valid path")
|
||||
for i in range(1, len(path)):
|
||||
if path[i] not in find_neighbors(path[i-1], grid):
|
||||
result = (False, "Path: invalid step from {} to {}".format(path[i-1], path[i]))
|
||||
break
|
||||
return result
|
||||
|
||||
def movement_status(path, grid):
|
||||
""" Determine the movement status of the path. """
|
||||
downward = 0
|
||||
upward = 0
|
||||
for i in range(1, len(path)):
|
||||
change = grid[path[i][0]][path[i][1]] - grid[path[i-1][0]][path[i-1][1]]
|
||||
if change > 0:
|
||||
upward += change
|
||||
elif change < 0:
|
||||
downward += change
|
||||
return (downward, upward)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
grid_index = input("Enter a grid index less than or equal to 3 (0 to end): ").strip()
|
||||
#grid_index = 1 # Debugging
|
||||
print(grid_index)
|
||||
grid_index = int(grid_index)
|
||||
grid = hw5_util.get_grid(int(grid_index))
|
||||
#print(grid) # Debugging
|
||||
"""
|
||||
grid = [[15, 16, 18, 19, 12, 11],\
|
||||
[13, 19, 23, 21, 16, 12],\
|
||||
[12, 15, 17, 19, 20, 10],\
|
||||
[10, 14, 16, 13, 9, 6]]
|
||||
"""
|
||||
|
||||
print_gride = input("Should the grid be printed (Y or N): ").strip()
|
||||
#print_gride = "Y" # Debugging
|
||||
print(print_gride)
|
||||
if print_gride.upper() == "Y":
|
||||
print("Grid {}".format(grid_index))
|
||||
show_grid(grid)
|
||||
|
||||
print("Grid has {} rows and {} columns".format(find_grid_size(grid)[0], find_grid_size(grid)[1]))
|
||||
|
||||
start_locations = hw5_util.get_start_locations(grid_index)
|
||||
"""start_locations = [(3, 3), (3, 0), (3, 5), (0, 2)]"""
|
||||
show_neighbors(start_locations, grid)
|
||||
"""find_neighbors((3, 3), grid) = [(2, 3), (3, 2), (3, 4)]"""
|
||||
|
||||
suggested_path = hw5_util.get_path(grid_index)
|
||||
#print("Suggested path: ", suggested_path) # Debugging
|
||||
"""Suggested path: [(3, 1), (3, 0), (2, 0), (1, 0), (1, 1), (0, 1), (0, 2), (1, 2)]"""
|
||||
print(path_validator(suggested_path, grid)[1])
|
||||
"""
|
||||
(True, 'Valid path')
|
||||
(False, 'Path: invalid step from (2, 4) to (3, 3)')
|
||||
"""
|
||||
|
||||
if path_validator(suggested_path, grid)[0]:
|
||||
downward, upward = movement_status(suggested_path, grid)
|
||||
downward = abs(downward)
|
||||
print("Downward {}\nUpward {}".format(downward, upward))
|
||||
```
|
||||
|
||||
### hw5_part2.py
|
||||
|
||||
```python
|
||||
import hw5_util
|
||||
|
||||
def find_height(point, grid):
|
||||
""" Find the height of a point in the grid. """
|
||||
return grid[point[0]][point[1]]
|
||||
|
||||
def find_grid_size(grid):
|
||||
""" Find the size of the grid. """
|
||||
return (len(grid), len(grid[0]))
|
||||
|
||||
def find_global_max(grid):
|
||||
""" Find the global maximum of the grid. """
|
||||
max_height = ((0,0), 0)
|
||||
for i in range(len(grid)):
|
||||
for j in range(len(grid[i])):
|
||||
if grid[i][j] > max_height[1]:
|
||||
max_height = ((i,j), grid[i][j])
|
||||
return max_height
|
||||
|
||||
def find_neighbors(point, grid):
|
||||
""" Find the neighbors of a point in the grid. """
|
||||
neighbors = []
|
||||
y, x = point
|
||||
max_y, max_x = find_grid_size(grid)
|
||||
neighbors.append((y-1, x)) if y-1 >= 0 else None
|
||||
neighbors.append((y, x-1)) if x-1 >= 0 else None
|
||||
neighbors.append((y, x+1)) if x+1 < max_x else None
|
||||
neighbors.append((y+1, x)) if y+1 < max_y else None
|
||||
return neighbors
|
||||
|
||||
def find_steepest_path(start, grid, max_step):
|
||||
""" Find the steepest path."""
|
||||
path = [start]
|
||||
current = start
|
||||
|
||||
while True:
|
||||
neighbors = find_neighbors(current, grid)
|
||||
next_step = None
|
||||
max_height_diff = 0
|
||||
|
||||
for n in neighbors:
|
||||
height_diff = find_height(n, grid) - find_height(current, grid)
|
||||
if height_diff > 0 and height_diff <= max_step and height_diff > max_height_diff:
|
||||
next_step = n
|
||||
max_height_diff = height_diff
|
||||
|
||||
if next_step is None:
|
||||
break
|
||||
|
||||
path.append(next_step)
|
||||
current = next_step
|
||||
return path
|
||||
|
||||
def find_gradual_path(start, grid, max_step):
|
||||
"""" Find the most gradual path."""
|
||||
path = [start]
|
||||
current = start
|
||||
|
||||
while True:
|
||||
neighbors = find_neighbors(current, grid)
|
||||
next_step = None
|
||||
min_height_diff = float("inf")
|
||||
|
||||
for n in neighbors:
|
||||
height_diff = find_height(n, grid) - find_height(current, grid)
|
||||
if height_diff > 0 and height_diff <= max_step and height_diff < min_height_diff:
|
||||
next_step = n
|
||||
min_height_diff = height_diff
|
||||
|
||||
if next_step is None:
|
||||
break
|
||||
|
||||
path.append(next_step)
|
||||
current = next_step
|
||||
return path
|
||||
|
||||
def show_path(path):
|
||||
"""" Display the path."""
|
||||
display = ""
|
||||
counter = 0
|
||||
for i in range(len(path)):
|
||||
if counter == 5:
|
||||
display += "\n"
|
||||
counter = 0
|
||||
display += "({}, {}) ".format(path[i][0], path[i][1])
|
||||
counter += 1
|
||||
print(display)
|
||||
|
||||
def find_path_end(path, grid):
|
||||
""" Find the end of the path."""
|
||||
end_point = path[-1]
|
||||
end_height = find_height(end_point, grid)
|
||||
|
||||
max_point, max_height = find_global_max(grid)
|
||||
|
||||
if end_height == max_height:
|
||||
return "global maximum"
|
||||
|
||||
neighbors = find_neighbors(end_point, grid)
|
||||
is_local_max = True
|
||||
for n in neighbors:
|
||||
if find_height(n, grid) > end_height:
|
||||
is_local_max = False
|
||||
break
|
||||
|
||||
if is_local_max:
|
||||
return "local maximum"
|
||||
else:
|
||||
return "no maximum"
|
||||
|
||||
def build_path_grid(grid, paths):
|
||||
""" Build the path grid."""
|
||||
rows, cols = find_grid_size(grid)
|
||||
path_grid = [[0] * cols for _ in range(rows)]
|
||||
|
||||
for path in paths:
|
||||
for point in path:
|
||||
y, x = point
|
||||
path_grid[y][x] += 1
|
||||
|
||||
return path_grid
|
||||
|
||||
def print_path_grid(path_grid):
|
||||
""" Display the path grid."""
|
||||
for i in range(len(path_grid)):
|
||||
row = " "
|
||||
for j in range(len(path_grid[i])):
|
||||
if path_grid[i][j] > 0:
|
||||
row += str(path_grid[i][j]) + " "
|
||||
else:
|
||||
row += "." + " "
|
||||
print(row.rstrip())
|
||||
|
||||
def print_path(path, path_type):
|
||||
""" Display the path."""
|
||||
print("{} path".format(path_type))
|
||||
show_path(path)
|
||||
print(find_path_end(path, grid))
|
||||
|
||||
if __name__ == "__main__":
|
||||
grid_index = input("Enter a grid index less than or equal to 3 (0 to end): ").strip()
|
||||
#grid_index = 1 # Debugging
|
||||
print(grid_index)
|
||||
grid_index = int(grid_index)
|
||||
grid = hw5_util.get_grid(int(grid_index))
|
||||
"""
|
||||
grid = [[15, 16, 18, 19, 12, 11],\
|
||||
[13, 19, 23, 21, 16, 12],\
|
||||
[12, 15, 17, 19, 20, 10],\
|
||||
[10, 14, 16, 13, 9, 6]]
|
||||
"""
|
||||
start_locations = hw5_util.get_start_locations(grid_index)
|
||||
|
||||
max_step_height = input("Enter the maximum step height: ").strip()
|
||||
#max_step_height = "4" # Debugging
|
||||
print(max_step_height)
|
||||
max_step_height = int(max_step_height)
|
||||
|
||||
print_path_grid_choice = input("Should the path grid be printed (Y or N): ").strip()
|
||||
#print_path_grid_choice = "Y" # Debugging
|
||||
print(print_path_grid_choice)
|
||||
print_path_grid_choice = print_path_grid_choice.upper()
|
||||
|
||||
print("Grid has {} rows and {} columns".format(find_grid_size(grid)[0], find_grid_size(grid)[1]))
|
||||
print("global max: {} {}".format(find_global_max(grid)[0], find_global_max(grid)[1]))
|
||||
|
||||
print("===")
|
||||
|
||||
paths = []
|
||||
|
||||
for start in start_locations:
|
||||
steepest = find_steepest_path(start, grid, max_step_height)
|
||||
gradual = find_gradual_path(start, grid, max_step_height)
|
||||
|
||||
print_path(steepest, "steepest")
|
||||
print("...")
|
||||
print_path(gradual, "most gradual")
|
||||
print("===")
|
||||
|
||||
paths.append(steepest)
|
||||
paths.append(gradual)
|
||||
|
||||
path_grid = build_path_grid(grid, paths)
|
||||
print("Path grid")
|
||||
print_path_grid(path_grid)
|
||||
```
|
||||
Reference in New Issue
Block a user