Skip to content
Snippets Groups Projects
Commit 83bb879c authored by Brady James Garvin's avatar Brady James Garvin
Browse files

Initial commit.

parents
Branches main
No related tags found
No related merge requests found
Showing
with 624 additions and 0 deletions
# Disable line-ending conversions for this repository.
* -text
# dependencies
/node_modules
# testing
/coverage
# production
/build
# environments
.env.local
.env.development.local
.env.test.local
.env.production.local
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# misc
*~
.DS_Store
[submodule "stylelint-config"]
path = stylelint-config
url = git@git.unl.edu:soft-core/soft-260/stylelint-config.git
[submodule "eslint-config"]
path = eslint-config
url = git@git.unl.edu:soft-core/soft-260/eslint-config.git
{
"folders": [
{
"path": "."
}
],
"settings": {
"files.eol": "LF",
"files.exclude": {
"**/node_modules": true
},
"files.trimFinalNewlines": true,
"files.trimTrailingWhitespace": true
}
}
# dependencies
/node_modules
# testing
/coverage
# production
/build
# environments
.env.local
.env.development.local
.env.test.local
.env.production.local
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# misc
*~
.DS_Store
# Algorithm Design Pattern
* Idea behind *divide and conquer* design:
* Handle a small input directly
* Break a large input into pieces
* (Non-overlapping is better)
* (Similar sizes are better)
* Recurse on zero or more of those pieces
* (Fewer recursions is better)
* Combine the subproblems' solutions
* (May require adding inputs)
* (May require adding outputs)
--------------------------------------------------------------------------------
# Divide-and-Conquer Sort
Problem: Sort a list in Θ(n log n) time.
## Design
```
In: [8, 7, 6, 9, 5]
Out: […, …, …, …, …]
/ \
/ \
In: […] In: […]
Out: […] Out: […]
/ \ / \
/ \ / \
In: […] In: […] In: […] In: […]
Out: […] Out: […] Out: […] Out: […]
/ \
/ \
In: […] In: […]
Out: […] Out: […]
```
## Analysis
* Even split:
T(n) = T(⌊n/2⌋) + T(⌈n/2⌉) + Θ(n) for n ≫ 0
T(n) ≈ 2T(n/2) + Θ(n) for n ≫ 0
T(n) = Θ(⋯)
* Uneven split:
T(n) = T(n - 1) + T(1) + Θ(n) for n ≫ 0
T(n) = T(n - 1) + Θ(1) + Θ(n) for n ≫ 0
T(n) = T(n - 1) + Θ(n) for n ≫ 0
T(n) = Θ(⋯)
--------------------------------------------------------------------------------
# Divide-and-Conquer Median
Problem: Find the median of an odd-length list in average-case Θ(n) time. Use views instead of slices to improve the algorithm's raw performance.
## Views
* *Views* act like collections but don't store any data; they just manipulate data from a larger original collection.
* Creating a view takes constant time, whereas creating a slice takes linear time (to copy the elements).
* So with a Θ(n) algorithm, using views instead of slices tangibly improves raw performance even on large inputs.
* And in an algorithm that is otherwise sublinear, slicing would be a bottleneck, whereas viewing would not.
## Design (first attempt)
```
In: [8, 7, 6, 9, 5]
Out: …
/ \
/ \
In: […] In: […]
Out: … Out: …
```
* Option A: We are missing information from the recursive calls. Add output(s) so that they can return that information.
* Option B: We need to ask different kinds of questions in the recursive calls. Add input(s) so that we can vary the question asked.
## Design (second attempt)
* More generally, we want …, so we add an input `k`:
```
In: [8, 7, 6, 9, 5], k = 2
Out: …
/ \
/ \
In: […], k = 2 In: […], k = 0
Out: … Out: …
```
## Partitioning
* We can efficiently split a collection into:
* a *pivot*,
* a subcollection of elements no larger than the pivot, and
* a subcollection of elements no smaller than the pivot.
* But we can't guarantee anything about the sizes of those subcollections.
## Design (third attempt)
```
In: [8, 7, 6, 9, 5], k = 2
In: […], k = 2
In: […], k = 2
Split: […], …, […]
Out: …
|
|
|
In: […], k = …
In: […], k = …
Split: […], …, […]
Out: …
|
|
|
In: […], k = …
In: […], k = …
Split: […], …, […]
Out: …
```
## Analysis
* Uneven split:
T(n) = T(n - 1) + Θ(n) for n ≫ 0
T(n) = Θ(⋯)
* Even split:
T(n) ≈ T(n/2) + Θ(n) for n ≫ 0
T(n) = Θ(⋯)
Source diff could not be displayed: it is too large. Options to address this: view the blob.
{
"name": "@unlsoft/divide-and-conquer",
"version": "1.0.0",
"description": "Starter code for the class on dynamic programming.",
"private": true,
"license": "UNLICENSED",
"scripts": {
"lint:css": "stylelint \"**/*.css\" \"**/*.module.css\" \"!coverage/**\"",
"lint:js": "eslint --max-warnings 0 ./src",
"lint": "run-s --continue-on-error lint:**",
"test-once": "react-scripts test --watchAll=false --coverage",
"test": "react-scripts test --watchAll --coverage",
"start": "react-scripts start",
"build": "react-scripts build",
"eject": "react-scripts eject"
},
"homepage": ".",
"dependencies": {
"@reduxjs/toolkit": "^1.6.0",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^12.0.0",
"@testing-library/user-event": "^13.1.9",
"classnames": "^2.3.1",
"npm-run-all": "^4.1.5",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-redux": "^7.2.4",
"react-router-dom": "^5.2.0",
"react-scripts": "^4.0.3",
"workbox-background-sync": "^5.1.3",
"workbox-broadcast-update": "^5.1.3",
"workbox-cacheable-response": "^5.1.3",
"workbox-core": "^5.1.3",
"workbox-expiration": "^5.1.3",
"workbox-google-analytics": "^5.1.3",
"workbox-navigation-preload": "^5.1.3",
"workbox-precaching": "^5.1.3",
"workbox-range-requests": "^5.1.3",
"workbox-routing": "^5.1.3",
"workbox-strategies": "^5.1.3",
"workbox-streams": "^5.1.3"
},
"devDependencies": {
"@unlsoft/eslint-config": "file:../eslint-config",
"@unlsoft/stylelint-config": "file:../stylelint-config",
"eslint-plugin-jest-dom": "^3.9.0",
"stylelint": "^13.13.1"
},
"stylelint": {
"extends": "@unlsoft/stylelint-config"
},
"eslintConfig": {
"extends": [
"react-app",
"@unlsoft/eslint-config/react"
]
},
"jest": {
"clearMocks": true,
"collectCoverageFrom": [
"src/features/**/*.js"
],
"resetMocks": false,
"restoreMocks": false
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<meta
name="description"
content="Starter code for the class on graph search."
/>
<meta name="theme-color" content="rgba(208 0 0 / 100%)" />
<link rel="icon" href="%PUBLIC_URL%/logo.svg" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo.svg" />
<title>Divide and Conquer in Class</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 152 152">
<rect x="0" y="0" width="152" height="152" fill="rgba(0 0 0 / 100%)" />
<path d="M147,1H90V42h10V75.673L53.532,2.393,52.648,1H2V42H12v66H2v41H62V108H52V74.336l46.467,73.271L99.351,149H150V108H140V42h10V1Z" stroke-width="3" stroke="rgba(255 255 255 / 100%)" fill="rgba(208 0 0 / 100%)">
</path>
</svg>
{
"short_name": "Divide and Conquer",
"name": "Divide and Conquer in Class",
"description": "Starter code for the class on divide and conquer.",
"icons": [
{
"src": "logo.svg",
"type": "image/svg+xml",
"sizes": "192x192 512x512",
"purpose": "any maskable"
}
],
"start_url": ".",
"display": "standalone",
"orientation": "portrait",
"theme_color": "rgba(208 0 0 / 100%)",
"background_color": "rgba(255 255 255 / 100%)"
}
import { Route } from 'react-router-dom';
import { Solution as SortingSolution } from './features/sorting/solution.js';
import { Solution as MedianSolution } from './features/median/solution.js';
// eslint-disable-next-line no-magic-numbers
const INPUT = [8, 7, 6, 9, 5];
export function App() {
return (
<>
<Route exact path={'/'}>
<h1>Sorting</h1>
<SortingSolution input={INPUT} />
<h1>Median</h1>
<MedianSolution input={INPUT} />
</Route>
</>
);
}
import { View } from './view.js';
function partition(sequence) {
console.assert(sequence.length > 0, 'Tried to partition an empty sequence.');
const pivot = sequence[0];
let i = 1;
let j = sequence.length - 1;
while (i !== 0) {
while (i < sequence.length && sequence[i] <= pivot) {
++i;
}
while (j > 0 && sequence[j] >= pivot) {
--j;
}
if (i >= j) {
i = 0;
}
const swap = sequence[i];
sequence[i] = sequence[j];
sequence[j] = swap;
}
return [
new View(sequence, 0, j),
sequence[j],
new View(sequence, j + 1, sequence.length),
];
}
export function kthSmallest(sequence, k) {
console.assert(
k >= 0 && k < sequence.length,
'Tried to find the kth smallest element when k is out of bounds.',
);
return sequence[0]; // TODO: stub
}
import PropTypes from 'prop-types';
import { kthSmallest } from './kthSmallest.js';
function formatList(list) {
return `[${list.join(', ')}]`;
}
export function Solution(props) {
return (
<>
<div>
<label>
Input: {formatList(props.input)}
</label>
</div>
<div>
<label>
Output: {kthSmallest([...props.input], Math.floor(props.input.length / 2))}
</label>
</div>
</>
);
}
Solution.propTypes = {
input: PropTypes.arrayOf(PropTypes.number).isRequired,
};
export class View {
constructor(sequence, low, high) {
const substrate = Array(high - low);
if (sequence instanceof View) {
substrate.original = sequence.original;
substrate.low = sequence.low + low;
substrate.high = sequence.low + high;
} else {
substrate.original = sequence;
substrate.low = low;
substrate.high = high;
}
const toOriginalIndex = (property) => {
if (typeof property === 'string' && /^\d+$/.test(property)) {
const result = substrate.low + Number(property);
if (result < substrate.high) {
return result;
}
}
return undefined;
};
return new Proxy(substrate, {
get(target, property, receiver) {
const originalIndex = toOriginalIndex(property);
if (originalIndex !== undefined) {
return target.original[originalIndex];
}
return Reflect.get(target, property, receiver);
},
set(target, property, value, receiver) {
const originalIndex = toOriginalIndex(property);
if (originalIndex !== undefined) {
// eslint-disable-next-line no-return-assign
return target.original[originalIndex] = value;
}
return Reflect.set(target, property, value, receiver);
},
});
}
}
import PropTypes from 'prop-types';
import { sort } from './sorting.js';
function formatList(list) {
return `[${list.join(', ')}]`;
}
export function Solution(props) {
return (
<>
<div>
<label>
Input: {formatList(props.input)}
</label>
</div>
<div>
<label>
Output: {formatList(sort(props.input))}
</label>
</div>
</>
);
}
Solution.propTypes = {
input: PropTypes.arrayOf(PropTypes.number).isRequired,
};
function merge(left, right) {
return []; // TODO: stub
}
export function sort(list) {
return list; // TODO: stub
}
:root {
/* Colors */
--letterbox-color: rgba(0 0 0 / 100%);
--app-background-color: rgba(239 239 239 / 100%);
--font-color: rgba(0 0 0 / 100%);
/* Sizes */
--minimum-app-size: 300px;
}
body {
margin: 0;
font-family: sans-serif;
text-align: center;
color: var(--font-color);
}
#root {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: var(--letterbox-color);
}
#portrait {
position: relative;
box-sizing: border-box;
margin: auto;
padding: 1em;
min-width: var(--minimum-app-size);
min-height: var(--minimum-app-size);
width: 100%;
height: 100%;
max-width: 62.5vh;
background: var(--app-background-color);
overflow-x: hidden;
overflow-y: scroll;
transform: scale(1);
}
import React from 'react';
import ReactDOM from 'react-dom';
import { HashRouter as Router } from 'react-router-dom';
import { App } from './app.js';
import './index.css';
import * as serviceWorkerRegistration from './serviceWorkerRegistration.js';
ReactDOM.render(
<React.StrictMode>
<Router>
<div id="portrait">
<App />
</div>
</Router>
</React.StrictMode>,
document.getElementById('root'),
);
// Learn more about service workers: https://cra.link/PWA
serviceWorkerRegistration.register();
/* eslint-disable no-restricted-globals, no-underscore-dangle */
// This service worker can be customized!
// See https://developers.google.com/web/tools/workbox/modules
// for the list of available Workbox modules, or add any other
// code you'd like.
// You can also remove this file if you'd prefer not to use a
// service worker, and the Workbox build step will be skipped.
import { clientsClaim } from 'workbox-core';
import { ExpirationPlugin } from 'workbox-expiration';
import { precacheAndRoute, createHandlerBoundToURL } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { StaleWhileRevalidate } from 'workbox-strategies';
clientsClaim();
// Precache all of the assets generated by your build process.
// Their URLs are injected into the manifest variable below.
// This variable must be present somewhere in your service worker file,
// even if you decide not to use precaching. See https://cra.link/PWA
precacheAndRoute(self.__WB_MANIFEST);
// Set up App Shell-style routing, so that all navigation requests
// are fulfilled with your index.html shell. Learn more at
// https://developers.google.com/web/fundamentals/architecture/app-shell
const fileExtensionRegexp = new RegExp('/[^/?]+\\.[^/]+$');
registerRoute(
// Return false to exempt requests from being fulfilled by index.html.
({ request, url }) => {
// If this isn't a navigation, skip.
if (request.mode !== 'navigate') {
return false;
} // If this is a URL that starts with /_, skip.
if (url.pathname.startsWith('/_')) {
return false;
} // If this looks like a URL for a resource, because it contains // a file extension, skip.
if (url.pathname.match(fileExtensionRegexp)) {
return false;
} // Return true to signal that we want to use the handler.
return true;
},
createHandlerBoundToURL(`${process.env.PUBLIC_URL}/index.html`),
);
const MAX_ENTRIES = 50;
// An example runtime caching route for requests that aren't handled by the
// precache, in this case same-origin .png requests like those from in public/
registerRoute(
// Add in any other file extensions or routing criteria as needed.
// Customize this strategy as needed, e.g., by changing to CacheFirst.
({ url }) => url.origin === self.location.origin && url.pathname.endsWith('.png'),
new StaleWhileRevalidate({
cacheName: 'images',
plugins: [
// Ensure that once this runtime cache reaches a maximum size the
// least-recently used images are removed.
new ExpirationPlugin({ maxEntries: MAX_ENTRIES }),
],
}),
);
// This allows the web app to trigger skipWaiting via
// registration.waiting.postMessage({type: 'SKIP_WAITING'})
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
// Any other custom service worker logic can go here.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment