-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 0d79c2a
Showing
4 changed files
with
384 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
# Josephus Problem | ||
|
||
## Introduction | ||
|
||
The Josephus Problem is a theoretical problem that involves a group of people standing in a circle. Every k-th person is eliminated in a sequential manner until only one person remains. This problem has historical significance and applications in computer science, particularly in algorithm design and data structures. | ||
|
||
This application provides an interactive visual representation of the Josephus Problem using a circular linked list, implemented in JavaScript and HTML. | ||
|
||
## Usage | ||
|
||
### Prerequisites | ||
|
||
- A modern web browser (Chrome, Firefox, Safari, etc.) | ||
- Ensure all files are in the same directory | ||
|
||
### Steps to Run the Application | ||
|
||
1. **Open the Application**: | ||
- Download or clone the repository to your local machine. | ||
- Navigate to the directory containing the files. | ||
- Open the `index.html` file in your web browser. | ||
|
||
2. **Input Members Count**: | ||
- On the main page, you will see a text box labeled "Members Count". | ||
- Enter the number of people (nodes) you want to include in the circle. For example, to simulate the problem with 10 people, enter `10`. | ||
|
||
3. **Start the Simulation**: | ||
- After entering the desired number of members, click the "Play" button to start the simulation. | ||
- The application will create a circular representation of the people standing in the circle. | ||
|
||
4. **Visualizing the Process**: | ||
- The nodes will be displayed in a circular formation on the screen. | ||
- The elimination process will begin, and each eliminated node will be marked visually (e.g., turning red or labeled as "Holder"). | ||
- The simulation will continue until only one node remains in the circle. | ||
|
||
5. **Observe the Result**: | ||
- The last remaining node is the "safe position" in the Josephus Problem. | ||
|
||
6. **Restart the Simulation**: | ||
- To run a new simulation, refresh the page and enter a new number of participants. | ||
|
||
### Example Use Case | ||
|
||
1. **Enter Members Count**: | ||
- Input `10` in the "Members Count" text box. | ||
|
||
2. **Play the Simulation**: | ||
- Click the "Play" button. | ||
- The application will display 10 nodes in a circle and begin the elimination process. | ||
- Observe how nodes are sequentially removed until one node is left. | ||
|
||
3. **Repeat**: | ||
- Refresh the page to restart the simulation and try different numbers of members to see how the safe position changes. | ||
|
||
## Circular Linked List | ||
|
||
The application uses a circular linked list to represent the group of people in the Josephus Problem. Here’s a brief explanation of how it works: | ||
|
||
- **Circular Linked List**: A linked list where the last node points back to the first node, forming a circle. This structure allows for continuous traversal of the list without reaching an endpoint. | ||
- **Node Elimination**: Starting from a specified point, nodes (people) are removed from the list at regular intervals (every k-th node). The process continues in a circular manner until only one node remains. | ||
- **Visualization**: The application visually marks the eliminated nodes, helping users understand the progression of the problem. | ||
|
||
By using this interactive application, you can better understand the mechanics of the Josephus Problem and visualize the elimination process in a clear and intuitive way. The circular linked list provides an efficient way to handle the continuous nature of the problem, making it an ideal choice for this application. | ||
|
||
## Alternative Access | ||
|
||
Users can also visit the application online at [naderidev.ir/sdds](http://naderidev.ir/gg) instead of running it locally. | ||
|
||
## Contributing | ||
|
||
Contributions are welcome! If you have any suggestions or improvements, feel free to create an issue or submit a pull request. |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
|
||
<head> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<!-- <meta http-equiv="refresh" content="1"> --> | ||
<title>Josephus Problem</title> | ||
<link href="bootstrap.min.css" rel="stylesheet"> | ||
<style> | ||
html, | ||
body { | ||
height: 100%; | ||
width: 100%; | ||
} | ||
|
||
.node { | ||
width: 3.5rem; | ||
height: 3.5rem; | ||
font-size: 10px; | ||
} | ||
.will-remove{ | ||
background-color: red!important; | ||
color: white; | ||
} | ||
</style> | ||
</head> | ||
|
||
<body> | ||
<div class="container-lg py-3 h-100"> | ||
<div class="row h-100"> | ||
<div class="col-12 my-2"> | ||
<div class="text-center"> | ||
<span class="fw-bolder fs-3 d-block">Josephus Problem</span> | ||
<span class="text-muted fs-6">Circular Linked List Representation</span> | ||
</div> | ||
</div> | ||
<div class="col-12 my-5"> | ||
<div id="rounded-table" class="mx-auto border border-secondary rounded-pill position-relative" | ||
style="height: 25rem; width: 25rem;"> | ||
|
||
</div> | ||
</div> | ||
<div class="col-12"> | ||
<div class="row justify-content-center"> | ||
<div class="col-2 m-1"> | ||
<input type="number" step="1" class="form-control" id="members-count" placeholder="Members Count"> | ||
</div> | ||
<button class="btn m-1 btn-success col-2" onclick="play(this)"> | ||
Play | ||
</button> | ||
</div> | ||
</div> | ||
<div class="col-12"> | ||
<div class="row text-center align-items-end h-100"> | ||
<div> | ||
<a class="fs-6 fw-bolder d-block text-decoration-none" href="https://github.com/naderidev" >Naderidev</span> | ||
<small class="text-muted d-block fw-light">Developed by</small> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
<script src="script.js"></script> | ||
</body> | ||
|
||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,239 @@ | ||
class CircularLinkedList { | ||
head = null; | ||
|
||
/* | ||
* Appends a node to the linked list | ||
* @param {Node} node - an instance of node. | ||
*/ | ||
append(node) { | ||
if (this.head === null) { | ||
node.link = node; | ||
} else { | ||
node.link = this.head.link | ||
this.head.link = node; | ||
} | ||
this.head = node; | ||
} | ||
|
||
/* | ||
* Appends a list of nodes to the linkd list | ||
* @param {Array[Node]} nodes - an array of nodes. | ||
*/ | ||
batchAppend(nodes) { | ||
nodes.forEach(n => this.append(n)); | ||
} | ||
|
||
/* | ||
* Pops the given node from the linkd list | ||
* @param {Node} nodes - an instance of node. | ||
* @return {boolean} - returns true of the node has poped and false if not. | ||
*/ | ||
popNode(node) { | ||
let flag = false; | ||
this.walk( | ||
current => { | ||
|
||
// Checks if the next node is the given node | ||
if (current.link === node) { | ||
|
||
// links the next node to next->next node. | ||
current.link = node.link; | ||
|
||
// if the given node is the head node, so set the node before removed node for head. | ||
if (node === this.head) this.head = current; | ||
|
||
// removes the node from memory | ||
delete node.prototype; | ||
|
||
// Sets the flag for successfully removed action. | ||
flag = true; | ||
|
||
return 0; | ||
} | ||
} | ||
) | ||
return flag; | ||
} | ||
|
||
/* | ||
* Walks in nodes and calls the given function with each node. | ||
* @param {callable} func - an callable function. | ||
*/ | ||
walk(func) { | ||
var temp = this.head; | ||
do { | ||
if (func(temp) === 0) break; | ||
temp = temp.link; | ||
} while (temp !== this.head); | ||
|
||
} | ||
|
||
/* | ||
* Holder shoots the next node and gives the gun to node after the victim node. | ||
*/ | ||
shoot() { | ||
let flag = false; | ||
this.walk((node) => { | ||
|
||
// Checks if the next node does not point to its self and if the current node is the holder. | ||
if (node.link !== node && node.data.isHolder) { | ||
|
||
// Takes the gun from the current holder | ||
node.data.isHolder = false; | ||
|
||
// Passes the gun to the next next node after the victim node. | ||
node.link.link.data.isHolder = true; | ||
|
||
// Marks the victim node for removal | ||
const will_remove = node.link.data.element; | ||
setTimeout(() => will_remove.classList.add("will-remove"), 1000); | ||
|
||
// Removes the victim node | ||
this.popNode(node.link); | ||
|
||
// sets the removal flag | ||
flag = true; | ||
return 0 | ||
} | ||
}); | ||
return flag; | ||
} | ||
|
||
/* | ||
* Walks on nodes and returns all of them in an array. | ||
* @param {Array[Node]} func - all nodes. | ||
*/ | ||
getNodes() { | ||
let nodes = []; | ||
this.walk(node => nodes.push(node)); | ||
return nodes; | ||
} | ||
|
||
/* | ||
* Prints all of the nodes. | ||
*/ | ||
printList() { | ||
this.walk(node => console.log(node)) | ||
|
||
} | ||
} | ||
|
||
class Node { | ||
|
||
// Node Data | ||
data = null; | ||
|
||
// The link to the next node | ||
link = null; | ||
|
||
constructor(data) { | ||
this.data = data; | ||
} | ||
} | ||
|
||
class ElementNode extends Node { | ||
constructor(id, isHolder) { | ||
super( | ||
{ | ||
id: id, | ||
isHolder: isHolder, | ||
element: null | ||
} | ||
); | ||
} | ||
} | ||
|
||
class NodeManager { | ||
constructor(members_count = 10, list = null, table = null) { | ||
this.list = list; | ||
this.table = table; | ||
|
||
// َAppends the generated nodes to the linkd list | ||
this.list.batchAppend(this.generateNodes(members_count)); | ||
|
||
// first render of nodes | ||
this.renderNodes(); | ||
|
||
|
||
} | ||
|
||
/* | ||
* Append and renders the given node to the table. | ||
*/ | ||
appendNodeElement(node) { | ||
const el = "<div class=\"row align-items-center text-center h-100\">" + | ||
" <div class=\"col-12 fs-5\">" + node.data.id + | ||
" </div>" + | ||
" <div class=\"col-12 border-top border-warning\">" + | ||
" <small>" + (node.data.isHolder ? "Holder" : "---") + "</small>" + | ||
" </div>" + | ||
" </div>" + | ||
"</div>"; | ||
const element = document.createElement("div") | ||
element.className = "bg-white node border border-warning overflow-hidden rounded-2 border-1 position-absolute" | ||
element.innerHTML = el; | ||
node.data.element = element; | ||
this.table.appendChild(element) | ||
} | ||
|
||
/* | ||
* Removes all of the nodes from the table and re-renders the remaining nodes. | ||
*/ | ||
renderNodes() { | ||
this.table.innerHTML = "" | ||
const nodes = this.list.getNodes(); | ||
nodes.forEach(n => this.appendNodeElement(n)) | ||
this.arrangeNodes(nodes) | ||
} | ||
|
||
/* | ||
* plays the game | ||
*/ | ||
play() { | ||
var p = setInterval(() => { | ||
this.renderNodes(); | ||
if (!this.list.shoot()) { | ||
clearInterval(p); | ||
} | ||
}, 4000) | ||
} | ||
|
||
/* | ||
* Arranges the nodes around the table | ||
*/ | ||
arrangeNodes(nodes) { | ||
const r = this.table.offsetWidth / 2; | ||
const y0 = r - 20; | ||
const x0 = r - 30; | ||
const d = 2 * Math.PI / nodes.length | ||
for (let i = 0; i < nodes.length; i++) { | ||
const element = nodes[i].data.element; | ||
element.style.bottom = r * Math.sin(d * i) + y0 + "px" | ||
element.style.left = r * Math.cos(d * i) + x0 + "px" | ||
} | ||
|
||
} | ||
|
||
/* | ||
* Generates the nodes with given length with random ids. | ||
* @param {Integer} length | ||
* @return {Array[Node]} - generated nodes. | ||
*/ | ||
generateNodes(length = 8) { | ||
const holderIndex = Math.floor(Math.random() * length); | ||
const startId = Math.floor(Math.random() * 100); | ||
return Array.from({ length: length }, (i, v) => new ElementNode(v + startId, (v === holderIndex))) | ||
} | ||
} | ||
|
||
function play(element) { | ||
const members_count = document.getElementById("members-count").value; | ||
if (members_count && members_count > 0) { | ||
new NodeManager( | ||
members_count, | ||
new CircularLinkedList(), | ||
document.getElementById("rounded-table") | ||
).play(); | ||
element.parentNode.remove(); | ||
} | ||
} |