mirror of
https://github.com/danbulant/introductionToProgramming
synced 2026-05-19 04:18:32 +00:00
week12
This commit is contained in:
parent
0690e7c705
commit
125e24b3b8
11 changed files with 304 additions and 94 deletions
81
common/Stopwatch.java
Normal file
81
common/Stopwatch.java
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
/******************************************************************************
|
||||
* Compilation: javac Stopwatch.java
|
||||
* Execution: java Stopwatch n
|
||||
* Dependencies: none
|
||||
*
|
||||
* A utility class to measure the running time (wall clock) of a program.
|
||||
*
|
||||
* % java8 Stopwatch 100000000
|
||||
* 6.666667e+11 0.5820 seconds
|
||||
* 6.666667e+11 8.4530 seconds
|
||||
*
|
||||
******************************************************************************/
|
||||
package common;
|
||||
|
||||
/**
|
||||
* The {@code Stopwatch} data type is for measuring
|
||||
* the time that elapses between the start and end of a
|
||||
* programming task (wall-clock time).
|
||||
*
|
||||
* See {@link StopwatchCPU} for a version that measures CPU time.
|
||||
*
|
||||
* @author Robert Sedgewick
|
||||
* @author Kevin Wayne
|
||||
*/
|
||||
|
||||
|
||||
public class Stopwatch {
|
||||
|
||||
private final long start;
|
||||
|
||||
/**
|
||||
* Initializes a new stopwatch.
|
||||
*/
|
||||
public Stopwatch() {
|
||||
start = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the elapsed CPU time (in seconds) since the stopwatch was created.
|
||||
*
|
||||
* @return elapsed CPU time (in seconds) since the stopwatch was created
|
||||
*/
|
||||
public double elapsedTime() {
|
||||
long now = System.currentTimeMillis();
|
||||
return (now - start) / 1000.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unit tests the {@code Stopwatch} data type.
|
||||
* Takes a command-line argument {@code n} and computes the
|
||||
* sum of the square roots of the first {@code n} positive integers,
|
||||
* first using {@code Math.sqrt()}, then using {@code Math.pow()}.
|
||||
* It prints to standard output the sum and the amount of time to
|
||||
* compute the sum. Note that the discrete sum can be approximated by
|
||||
* an integral - the sum should be approximately 2/3 * (n^(3/2) - 1).
|
||||
*
|
||||
* @param args the command-line arguments
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
int n = Integer.parseInt(args[0]);
|
||||
|
||||
// sum of square roots of integers from 1 to n using Math.sqrt(x).
|
||||
Stopwatch timer1 = new Stopwatch();
|
||||
double sum1 = 0.0;
|
||||
for (int i = 1; i <= n; i++) {
|
||||
sum1 += Math.sqrt(i);
|
||||
}
|
||||
double time1 = timer1.elapsedTime();
|
||||
StdOut.printf("%e (%.2f seconds)\n", sum1, time1);
|
||||
|
||||
// sum of square roots of integers from 1 to n using Math.pow(x, 0.5).
|
||||
Stopwatch timer2 = new Stopwatch();
|
||||
double sum2 = 0.0;
|
||||
for (int i = 1; i <= n; i++) {
|
||||
sum2 += Math.pow(i, 0.5);
|
||||
}
|
||||
double time2 = timer2.elapsedTime();
|
||||
StdOut.printf("%e (%.2f seconds)\n", sum2, time2);
|
||||
}
|
||||
}
|
||||
2
main.typ
2
main.typ
|
|
@ -15,7 +15,7 @@ Collection of solutions to programming exercises as part of Introduction to Prog
|
|||
)
|
||||
|
||||
#{
|
||||
let count = 11;
|
||||
let count = 12;
|
||||
for week in range(1, count + 1).filter(it => it != 8) {
|
||||
let a = "./week" + str(week) + "/doc.typ"
|
||||
include a
|
||||
|
|
|
|||
38
week11/Clock.java
Normal file
38
week11/Clock.java
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import common.StdDraw;
|
||||
|
||||
public class Clock {
|
||||
public static void main(String[] args) {
|
||||
StdDraw.setScale(-1, 1);
|
||||
StdDraw.enableDoubleBuffering();
|
||||
|
||||
while(true) {
|
||||
var time = System.currentTimeMillis();
|
||||
var ms = (int) (time % 1000);
|
||||
// this could be changed to doubles to have smooth movement
|
||||
var seconds = (int) (time / 1000 % 60);
|
||||
var minutes = (int) (time / 60_000 % 60);
|
||||
var hours = (int) (time / 3_600_000 % 12);
|
||||
var secondsAngle = (double) seconds / 60 * 2*Math.PI;
|
||||
var minutesAngle = (double) minutes / 60 * 2*Math.PI;
|
||||
var hoursAngle = (double) hours / 12 * 2*Math.PI;
|
||||
|
||||
StdDraw.clear();
|
||||
StdDraw.setPenColor(StdDraw.BLACK);
|
||||
StdDraw.setPenRadius(0.004);
|
||||
StdDraw.line(0, 0, Math.sin(minutesAngle) * 0.8, Math.cos(minutesAngle) * 0.8);
|
||||
StdDraw.line(0, 0, Math.sin(hoursAngle), Math.cos(hoursAngle));
|
||||
|
||||
StdDraw.setPenColor(StdDraw.RED);
|
||||
StdDraw.setPenRadius(0.002);
|
||||
StdDraw.line(0, 0, Math.sin(secondsAngle), Math.cos(secondsAngle));
|
||||
|
||||
StdDraw.setPenRadius(0.004);
|
||||
StdDraw.setPenColor(StdDraw.BLACK);
|
||||
StdDraw.circle(0, 0, 1);
|
||||
|
||||
StdDraw.show();
|
||||
StdDraw.pause(1000 - ms);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
26
week11/RandomSequenceChange.java
Normal file
26
week11/RandomSequenceChange.java
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import common.StdRandom;
|
||||
|
||||
public class RandomSequenceChange {
|
||||
public static void main(String[] args) {
|
||||
var arrayLength = 5;
|
||||
var rounds = 100;
|
||||
var data = new int[arrayLength];
|
||||
for(var i = 0; i < data.length; i++)
|
||||
data[i] = i;
|
||||
// number of times any repetition was found after a random shuffle
|
||||
var total = 0;
|
||||
for(var i = 0; i < rounds; i++) {
|
||||
StdRandom.shuffle(data);
|
||||
total += (countSequences(data) > 0) ? 1 : 0;
|
||||
}
|
||||
var chance = 1 - (total / (double) rounds);
|
||||
System.out.printf("The chance of no repetition is %s%%\n", chance * 100);
|
||||
}
|
||||
|
||||
static int countSequences(int[] data) {
|
||||
var output = 0;
|
||||
for(var i = 1; i < data.length; i++)
|
||||
if(data[i] == data[i - 1] + 1) output++;
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#show: template
|
||||
|
||||
= Week 10
|
||||
= Week 11
|
||||
|
||||
== Exercise 1.3.45
|
||||
|
||||
|
|
@ -15,7 +15,7 @@ with a small population—say, $x = 0.01$—and study the result of iterating th
|
|||
$x = 1 - 1/r$ ? Can you say anything about the population when $r$ is $3.5$? $3.8$? $5$?
|
||||
|
||||
#table(
|
||||
columns: 3,
|
||||
columns: 4,
|
||||
$3.5$,
|
||||
$3.8$,
|
||||
$5$,
|
||||
|
|
@ -26,15 +26,21 @@ $x = 1 - 1/r$ ? Can you say anything about the population when $r$ is $3.5$? $3.
|
|||
[Sustained $1-1/r$]
|
||||
)
|
||||
|
||||
#embedClass(name: "Chaos")
|
||||
|
||||
== Exercise 1.4.26
|
||||
|
||||
_Music shuffling_. You set your music player to shuffle mode. It plays each of
|
||||
the n songs before repeating any. Write a program to estimate the likelihood that
|
||||
the $n$ songs before repeating any. Write a program to estimate the likelihood that
|
||||
you will not hear any sequential pair of songs (that is, song 3 does not follow song
|
||||
2, song 10 does not follow song 9, and so on).
|
||||
|
||||
#embedClass(name: "RandomSequenceChange")
|
||||
|
||||
== Exercise 1.5.32
|
||||
|
||||
_Clock_. Write a program that displays an animation of the second, minute,
|
||||
and hour hands of an analog clock. Use the method `StdDraw.pause(1000)` to
|
||||
update the display roughly once per second.
|
||||
|
||||
#embedClass(name: "Clock")
|
||||
119
week12/Dict.java
119
week12/Dict.java
|
|
@ -1,102 +1,47 @@
|
|||
package week12;
|
||||
|
||||
public class Dict<K, V> {
|
||||
import java.util.Arrays;
|
||||
|
||||
public class Dict<K extends Comparable<K>, V> {
|
||||
@SuppressWarnings("unchecked")
|
||||
private Pair<K, V>[] data = new Pair[4];
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.printf("%s %s %s\n", 1, 4, getIndex(1, 4));
|
||||
System.out.printf("%s %s %s\n", 1, 8, getIndex(1, 8));
|
||||
System.out.printf("%s %s %s\n", 1073741824, 4, getIndex(1073741824, 4));
|
||||
System.out.printf("%s %s %s\n", 1073741824, 8, getIndex(1073741824, 8));
|
||||
System.out.printf("%s %s %s\n", Integer.MIN_VALUE, 4, getIndex(Integer.MIN_VALUE, 4));
|
||||
System.out.printf("%s %s %s\n", Integer.MIN_VALUE, 8, getIndex(Integer.MIN_VALUE, 8));
|
||||
System.out.printf("%s %s %s\n", Integer.MAX_VALUE, 4, getIndex(Integer.MAX_VALUE, 4));
|
||||
System.out.printf("%s %s %s\n", Integer.MAX_VALUE, 8, getIndex(Integer.MAX_VALUE, 8));
|
||||
}
|
||||
private int ptr = 0;
|
||||
|
||||
public V get(K key) {
|
||||
var hashCode = key.hashCode();
|
||||
var hash = hashCode;
|
||||
// try getting a pair as long as there's a hash collision
|
||||
while(true) {
|
||||
var index = getIndex(hash);
|
||||
var pair = data[index];
|
||||
if(pair == null) return null;
|
||||
if(pair.first.equals(key)) return pair.second;
|
||||
var secondHash = pair.first.hashCode();
|
||||
if(hashCode != secondHash) return null;
|
||||
// this can overflow but should always be in range of the array when used with getIndex
|
||||
hash += 1;
|
||||
var index = binarySearch(key);
|
||||
if(index >= 0) return data[index].second;
|
||||
return null;
|
||||
}
|
||||
|
||||
// adapted from Arrays.binarySearch
|
||||
// edited as Arrays.binarySearch doesn't accept a mapping function
|
||||
private int binarySearch(K key) {
|
||||
int low = 0;
|
||||
int high = data.length - 1;
|
||||
|
||||
while (low <= high) {
|
||||
int mid = (low + high) >>> 1;
|
||||
var midVal = data[mid];
|
||||
int cmp = PairComparator.compare(midVal == null ? null : midVal.first, key);
|
||||
if (cmp < 0)
|
||||
low = mid + 1;
|
||||
else if (cmp > 0)
|
||||
high = mid - 1;
|
||||
else
|
||||
return mid;
|
||||
}
|
||||
}
|
||||
|
||||
private static int getIndex(int hashCode, int length) {
|
||||
// using this instead of modulo allows us to simply insert nulls on every odd number when doubling array instead of having to rehash
|
||||
// this changes the range of number from <MIN_VALUE, MAX_VALUE> to <0, data.length>
|
||||
long hash = hashCode;
|
||||
hash -= Integer.MIN_VALUE; // make the number positive, i.e. from 0 to 2*MAX_VALUE+1 (MIN_VALUE = -MAX_VALUE - 1)
|
||||
hash *= length; // this may cause overflow in int
|
||||
hash /= Integer.MAX_VALUE; // this will always put the number back into int range, as array length is at most MAX_VALUE
|
||||
hash /= 2;
|
||||
return (int)hash;
|
||||
}
|
||||
|
||||
private int getIndex(int hashCode) {
|
||||
return getIndex(hashCode, data.length);
|
||||
return -(low + 1); // key not found.
|
||||
}
|
||||
|
||||
private void increaseBackingSize() {
|
||||
@SuppressWarnings("unchecked")
|
||||
Pair<K, V>[] newData = new Pair[data.length * 2];
|
||||
for(var i = 0; i < data.length; i++) {
|
||||
newData[i * 2] = data[i];
|
||||
}
|
||||
data = newData;
|
||||
data = Arrays.copyOf(data, data.length * 2);
|
||||
}
|
||||
|
||||
public void put(K key, V value) {
|
||||
var hashCode = key.hashCode();
|
||||
var hash = hashCode;
|
||||
var index = getIndex(hash);
|
||||
var existingPair = data[index];
|
||||
var newPair = new Pair<>(key, value);
|
||||
if(existingPair != null && existingPair.first.equals(key)) {
|
||||
data[index] = existingPair.withSnd(value);
|
||||
return; // some of these returns aren't needed but directly show exit points of the function at a glance
|
||||
} else if(existingPair != null) {
|
||||
var originalHash = existingPair.first.hashCode();
|
||||
if(hash == originalHash) {
|
||||
// full collision of hash, cannot solve so add +1 to hash and try to insert it again
|
||||
// a different possible solution is to use 2d arrays, with the topmost being a mapping of hashcodes to arrays of pairs
|
||||
|
||||
// insert the pair into first empty space after hash collisions. If there's something else though, increase backing size and try again, it should fit right after then.
|
||||
// Simply trying to put again recomputes stuff needlessly but simplifies code.
|
||||
while(true) {
|
||||
hash += 1;
|
||||
index = getIndex(hash);
|
||||
existingPair = data[index];
|
||||
if(existingPair == null) {
|
||||
data[index] = newPair;
|
||||
return;
|
||||
}
|
||||
originalHash = existingPair.first.hashCode();
|
||||
if(hash == originalHash) {
|
||||
continue;
|
||||
} else {
|
||||
increaseBackingSize();
|
||||
put(key, value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// a better algorithm would be to calculate the required length for the collision to disappear, but this should also work eventually and is simpler
|
||||
increaseBackingSize();
|
||||
put(key, value);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
data[index] = newPair;
|
||||
}
|
||||
if(data.length >= ptr) increaseBackingSize();
|
||||
data[ptr] = new Pair<>(key, value);
|
||||
ptr += 1;
|
||||
// O(n) when almost sorted, which would be the case here. Allows use of binarySearch
|
||||
Arrays.sort(data, new PairComparator<>());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
27
week12/Multiset.java
Normal file
27
week12/Multiset.java
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package week12;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class Multiset<E> {
|
||||
Map<E, Integer> data = new HashMap<>();
|
||||
|
||||
public int count(E e) {
|
||||
var entry = data.get(e);
|
||||
return entry == null ? 0 : entry.intValue();
|
||||
}
|
||||
|
||||
public void add(E e) {
|
||||
data.compute(e, (key, num) -> num == null ? 1 : num+1);
|
||||
}
|
||||
|
||||
public void remove(E e) {
|
||||
var entry = data.get(e);
|
||||
if(entry == null || entry <= 1) data.remove(e);
|
||||
else data.put(e, entry - 1);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return data.size();
|
||||
}
|
||||
}
|
||||
32
week12/Pair.java
Normal file
32
week12/Pair.java
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
package week12;
|
||||
|
||||
class Pair<A, B> {
|
||||
final A first;
|
||||
final B second;
|
||||
|
||||
Pair(A first, B second) {
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
|
||||
// these getters doesn't seem to be needed, stdlib doesn't usually use getters for final values and instead just makes them public to be used directly
|
||||
public A getFirst() {
|
||||
return first;
|
||||
}
|
||||
|
||||
public B getSecond() {
|
||||
return second;
|
||||
}
|
||||
|
||||
public Pair<B, A> swap() {
|
||||
return new Pair<B, A>(second, first);
|
||||
}
|
||||
|
||||
public<C> Pair<C, B> withFst(C first) {
|
||||
return new Pair<C, B>(first, second);
|
||||
}
|
||||
|
||||
public<C> Pair<A, C> withSnd(C second) {
|
||||
return new Pair<A, C>(first, second);
|
||||
}
|
||||
}
|
||||
17
week12/PairComparator.java
Normal file
17
week12/PairComparator.java
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package week12;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
public class PairComparator<A extends Comparable<A>, B> implements Comparator<Pair<A, B>> {
|
||||
@Override
|
||||
public int compare(Pair<A, B> o1, Pair<A, B> o2) {
|
||||
return compare(o1 == null ? null : o1.first, o2 == null ? null : o2.first);
|
||||
}
|
||||
|
||||
static <K extends Comparable<K>> int compare(K firstKey, K secondKey) {
|
||||
if(firstKey == secondKey) return 0;
|
||||
if(firstKey == null) return 1;
|
||||
if(secondKey == null) return -1;
|
||||
return firstKey.compareTo(secondKey);
|
||||
}
|
||||
}
|
||||
1
week12/common
Symbolic link
1
week12/common
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../common
|
||||
37
week12/doc.typ
Normal file
37
week12/doc.typ
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#import "./common/common.typ" : *
|
||||
|
||||
#show: template
|
||||
|
||||
= Week 12
|
||||
|
||||
== Exercise 12.07
|
||||
|
||||
Write a class `Pair` with two type parameters `A` and `B` to represent an immutable pair of values (i.e. the class should have two `final` fields of type `A` and `B`).
|
||||
|
||||
- Add an appropriate constructor and getter methods.
|
||||
- Do not add any setters, as the class should be immutable.
|
||||
- Add a method `swap` to the `Pair` class. The `swap` method should return a *new* pair where the first component becomes the second component and vice versa. For example, for the pair `(true, 42)` the method should return `(42, true)`.
|
||||
- Add methods `withFst` and `withSnd` to the Pair class. Each method should take a type parameter `C` and return a new pair where the appropriate component has been updated. For example, calling `withFst` with the integer `42` on the pair `(true, "Hello World")` should return `(42, "Hello World")`.
|
||||
|
||||
== Exercise 12.08
|
||||
|
||||
Write a class `Dict` that takes two type parameters `K` and `V` to represent a dictionary, i.e. a mapping from items of type `K` to items of type `V`. Internally, the dictionary should maintain a single array of pairs of type `Pair<K, V>`. Add the following methods:
|
||||
|
||||
- `V get(K key)` returns the value associated with the given key, or null if the key is not found.
|
||||
- `void put(K key, V value)` updates the dictionary with a mapping from the key to the value. If the key already exists, its value is updated. Otherwise, a new pair is added.
|
||||
|
||||
You may assume the `Dict` can contain at most 100 entries.
|
||||
|
||||
== Exercise 12.16
|
||||
|
||||
Write a class, which takes one type parameter `E`, to represent a multiset. A multiset is a set that counts how many times it contains each of its elements. Add the following methods:
|
||||
|
||||
- `int count(E e)` returns the number of times the element `e` occurs in the multiset.
|
||||
- `void add(E e)` adds the element `e` to the multiset. (Adding increments its count.)
|
||||
- `void remove(E e)` removes the element `e` from the multiset. (Removing decrements its count.)
|
||||
- `int size()` returns the number of different elements in the set (non-duplicate count).
|
||||
|
||||
An element can never occur a negative number of times in a multiset.
|
||||
|
||||
_Hint: Use an internal map of type `Map<E, Integer>`._
|
||||
|
||||
Loading…
Reference in a new issue