diff --git a/Submissions/002771818_Yiheng_Ye/Assignment3.ipynb b/Submissions/002771818_Yiheng_Ye/Assignment3.ipynb new file mode 100644 index 0000000..f19e481 --- /dev/null +++ b/Submissions/002771818_Yiheng_Ye/Assignment3.ipynb @@ -0,0 +1,678 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "f309d9f1-53cd-4bc3-9fa6-9035ec44dc8d", + "metadata": {}, + "source": [ + "Question 1:\n", + "Give a brief definitions for the following:\n", + "1. Max-Flow\n", + "2. Min-cut theorem\n", + "3. Network Flow\n", + "4. Bellman-Ford\n", + "5. Dijkstra's Algorithm" + ] + }, + { + "cell_type": "markdown", + "id": "2fb9b2c8-6261-4ecb-a7e1-ec27affb3922", + "metadata": {}, + "source": [ + "Solution:\n", + "1&3:In a directed graph, a network has two main vertices, source and sink. Source only has out-direction edges and sink only has in-direction edges. Edges can be treated as pipes, in which flow can not exceed a pipe's capacity(weight).\n", + " So the max-flow means the maximum flow that origins from source and passes to sink.\n", + " \n", + "2: we can cut the graph G into two sets S and T. the maximum flow of G equals to the minmum cut capacity of all possible cut sets.\n", + "\n", + "4&5: Dijkstra's algorithm can find the shortest path between two vetices in a weighted graph. The graph's weights have to be non-negative values.\n", + " Bellman-Ford is similar to Dijkstra's but can work in graph which has negative values and stops in cycle generated negative values. " + ] + }, + { + "cell_type": "markdown", + "id": "36937d07-5a0a-4d4d-9fcd-70b9242933df", + "metadata": {}, + "source": [ + "reflection:\n", + " These terms are fundamental in graph theory and network analysis.Understanding these terms is crucial for various applications in computer science, optimization, and network analysis." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fdf120c0-d106-40f5-851a-0d3737721765", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "38893e3b-8c83-4956-9b18-c3c3012fe1d4", + "metadata": {}, + "source": [ + "Question2:Use the Bellman-Ford algorithm to find the shortest path from node 0 to 3 in the weighted directed graph above. Show your work\n", + "\n", + "adjency matrix:\n", + " 0 1 2 3 4 \n", + "0 0 2 6 7 0\n", + "1 0 0 0 3 6\n", + "2 0 0 0 0 1\n", + "3 0 0 0 0 5\n", + "4 0 0 0 0 0\n" + ] + }, + { + "cell_type": "markdown", + "id": "f115152c-8fcc-4ba7-99eb-ef277a5c3398", + "metadata": {}, + "source": [ + "solution:\n", + "\n", + "persudo code\n", + " initializing: dis_0_0=0,otherwise,dis_0_v=inf\n", + " for i from 1 to 4:\n", + " for edges(i,j) in graph:\n", + " if edge(i,j) + dis of noted node i < dis of noted node j:\n", + " dis of noted node j = edge(i,j) + dis of noted node i\n", + " end\n", + " end\n", + " \n", + " for edges(i,j) in graph:\n", + " if edge(i,j) + dis of noted node i < dis of noted node j:\n", + " report \"negative-weight cycle\"\n", + " end\n", + " ##after n-1 loop, paths have been optimized, if there exists another path which make distance smaller, then there must be a negative cycle.\n", + " \n", + "example:\n", + " 0 1 2 3 4 \n", + " 0 inf inf inf inf\n", + " 0 2 6 7 inf\n", + " 0 2 6 5 7\n", + " 0 2 6 5 7 " + ] + }, + { + "cell_type": "markdown", + "id": "fb81e11e-0e3f-4af0-854b-f82c81dff0e6", + "metadata": {}, + "source": [ + "reflection:\n", + " The algorithm works by iteratively relaxing edges in the graph, which means it attempts to improve the estimates of the shortest paths by considering each edge multiple times. It continues this process until no further improvements can be made or until it detects the presence of negative weight cycles. However, this example does not have a negative cycle, so this part of function is not shown." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "94165aff-a49a-402a-ac4c-3c2a6ea039ab", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "3de7c975-d22c-4973-9e14-ed1c427f4fa0", + "metadata": {}, + "source": [ + "Question3:\n", + "Use the Ford-Fulkerson algorithm to find the maximum flow from node s to t in the weighted directed graph.\n", + "\n", + "\n", + "adjency matrix:\n", + "\n", + " s 1 2 3 4 t\n", + "s 0 4 2 0 0 0\n", + "1 0 0 1 2 4 0\n", + "2 0 0 0 0 2 0\n", + "3 0 0 0 0 0 3\n", + "4 0 0 0 0 0 3\n", + "t 0 0 0 0 0 0\n" + ] + }, + { + "cell_type": "markdown", + "id": "88494383-09f7-4abf-a4b4-47d9fcf7b8eb", + "metadata": {}, + "source": [ + "solution:\n", + "origin\n", + "s-1-4-t flow 3\n", + "s-1-3-5 flow 1\n", + "s-2-4-1-3 flow 1(by residual graph)\n", + "no vertice connected, end\n", + "the maximum flow is 5.\n", + "\n", + "code:\n", + "# Python program for implementation \n", + "# of Ford Fulkerson algorithm\n", + "from collections import defaultdict\n", + " \n", + "# This class represents a directed graph \n", + "# using adjacency matrix representation\n", + "class Graph:\n", + " \n", + " def __init__(self, graph):\n", + " self.graph = graph # residual graph\n", + " self. ROW = len(graph)\n", + " # self.COL = len(gr[0])\n", + " \n", + " '''Returns true if there is a path from source 's' to sink 't' in\n", + " residual graph. Also fills parent[] to store the path '''\n", + " \n", + " def BFS(self, s, t, parent):\n", + " \n", + " # Mark all the vertices as not visited\n", + " visited = [False]*(self.ROW)\n", + " \n", + " # Create a queue for BFS\n", + " queue = []\n", + " \n", + " # Mark the source node as visited and enqueue it\n", + " queue.append(s)\n", + " visited[s] = True\n", + " \n", + " # Standard BFS Loop\n", + " while queue:\n", + " \n", + " # Dequeue a vertex from queue and print it\n", + " u = queue.pop(0)\n", + " \n", + " # Get all adjacent vertices of the dequeued vertex u\n", + " # If a adjacent has not been visited, then mark it\n", + " # visited and enqueue it\n", + " for ind, val in enumerate(self.graph[u]):\n", + " if visited[ind] == False and val > 0:\n", + " # If we find a connection to the sink node, \n", + " # then there is no point in BFS anymore\n", + " # We just have to set its parent and can return true\n", + " queue.append(ind)\n", + " visited[ind] = True\n", + " parent[ind] = u\n", + " if ind == t:\n", + " return True\n", + " \n", + " # We didn't reach sink in BFS starting \n", + " # from source, so return false\n", + " return False\n", + " \n", + " \n", + " # Returns the maximum flow from s to t in the given graph\n", + " def FordFulkerson(self, source, sink):\n", + " \n", + " # This array is filled by BFS and to store path\n", + " parent = [-1]*(self.ROW)\n", + " \n", + " max_flow = 0 # There is no flow initially\n", + " \n", + " # Augment the flow while there is path from source to sink\n", + " while self.BFS(source, sink, parent) :\n", + " \n", + " # Find minimum residual capacity of the edges along the\n", + " # path filled by BFS. Or we can say find the maximum flow\n", + " # through the path found.\n", + " path_flow = float(\"Inf\")\n", + " s = sink\n", + " while(s != source):\n", + " path_flow = min (path_flow, self.graph[parent[s]][s])\n", + " s = parent[s]\n", + " \n", + " # Add path flow to overall flow\n", + " max_flow += path_flow\n", + " \n", + " # update residual capacities of the edges and reverse edges\n", + " # along the path\n", + " v = sink\n", + " while(v != source):\n", + " u = parent[v]\n", + " self.graph[u][v] -= path_flow\n", + " self.graph[v][u] += path_flow\n", + " v = parent[v]\n", + " \n", + " return max_flow" + ] + }, + { + "cell_type": "markdown", + "id": "04a4fde2-f692-4295-8c82-899e575bfd56", + "metadata": {}, + "source": [ + "reflection:\n", + "The algorithm operates by repeatedly finding augmenting paths in the residual graph, which is derived from the original graph and represents the remaining capacity along each edge after the flow has been sent. The algorithm incrementally increases the flow along these paths until no more augmenting paths can be found. The maximum flow is reached when there are no more augmenting paths to be discovered in the residual graph." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "56dae1ad-7a41-47fc-914e-c94a23207c6b", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "c205e9c2-2731-4acf-bca2-bf7a01cc7bb1", + "metadata": {}, + "source": [ + "Question4:\n", + "Use the Preflow-Push (Push–relabel) maximum flow algorithm to find the maximum flow from node A to E in the weighted directed graph below. Show your work. \n", + "\n", + " A B C D E\n", + "A 0 10 8 0 0\n", + "B 0 0 0 2 0\n", + "C 0 0 0 5 0\n", + "D 0 0 0 0 5\n", + "E 0 0 0 0 0\n" + ] + }, + { + "cell_type": "markdown", + "id": "1d7d3cef-a9b4-42e4-801a-9926272d8f30", + "metadata": {}, + "source": [ + "solution:\n", + "{A,h=5}\n", + "f(A-B)=10 ef(B)=10\n", + "f(A-c)=8 ef(c)=8\n", + "\n", + "{B,h=1}\n", + "f(B-D)=2 ef(D)=2 ef(B)=8\n", + "\n", + "{C,h=1}\n", + "f(C-D)=5 ef(C)=3 ef(D)=7\n", + "\n", + "{D,h=1}\n", + "f(D-E)=5 ef(D)=2 ef(E)=5\n", + "\n", + "{D,h=2}\n", + "f(D-B)=2 ef(D)=0 ef(B)=10\n", + "\n", + "{B,h=6}\n", + "f(B-A)=10 ef(B)=0\n", + "\n", + "{C,h=6}\n", + "f(C-A)=3 ef(C)=0" + ] + }, + { + "cell_type": "markdown", + "id": "9cbec71a-b2bc-4ed0-b075-72f311fbbc0e", + "metadata": {}, + "source": [ + "reflection:\n", + "This is a simplified example to demonstrate the Preflow-Push algorithm.\n", + "Anyway ,gpt give some indrect answer:\n", + "\n", + "\n", + "Initialize preflow (excess) and height (distance) labels:\n", + "\n", + "Excess: All nodes except the source are initialized with excess 0.\n", + "Height: A is set to the highest height, and all other nodes are set to 0.\n", + "Push excess flow from the source (A) to its neighbors (B and C) if there is capacity:\n", + "\n", + "Push 8 units of flow from A to C, reducing the capacity of edge AC to 0.\n", + "Push 2 units of flow from A to B, reducing the capacity of edge AB to 8.\n", + "Update excess and deficit at C and B.\n", + "Relabel nodes: Since B and C have excess, but there are no available paths from C, we relabel node C's height to 1. This allows B to send more flow.\n", + "\n", + "Push excess flow from C to D, reducing the capacity of edge CD to 3. Update excess and deficit at D.\n", + "\n", + "Push excess flow from B to D, reducing the capacity of edge BD to 0.\n", + "\n", + "Relabel node B to height 2.\n", + "\n", + "Push excess flow from C to D, which increases the flow on edge CD to 5.\n", + "\n", + "Push excess flow from D to E, reducing the capacity of edge DE to 0.\n", + "\n", + "The algorithm terminates, and the maximum flow from A to E is 5 units." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0ea33262-ba96-4873-927a-6fc42f2c4d51", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "4f8b2cc3-2af8-4a4a-a541-a90d45971974", + "metadata": {}, + "source": [ + "Question5:\n", + "Consider a flow network with unit capacity edges. That is a directed graph G = (V,E) with a source vertex s € V, and target/sink vertex t € V and every edge e € E has cost ce = 1. Given an integer parameter k <= |E|, your goal is to reduce the capacity of the flow network from s to t as much as possible by deleting k edges. That is, you should find a subset of edges M from E with cardinality k (i.e. |M|=k and M is a subset of E) such that the flow from s-t in G’ = (V,E-M) is as small as possible. \n", + "Find a polynomial-time algorithm such that the flow from s-t in G’ = (V,E-M) is as small as possible." + ] + }, + { + "cell_type": "markdown", + "id": "b6b36794-e56c-41bc-972d-2c9230ab40e5", + "metadata": {}, + "source": [ + "Solution:\n", + "Basically, the first impression is that to cut all the edges directly connected to source and sink until there is only one edge left(otherwise it's not a networkflow anymore). To reduce the flow more efficiently, we can randomly choose source or sink, and cut edges from one of them first, until there is only one left, and then cut another one. Or we can detect which node has more edges, then cut the one with less edges.(since every edge has cost 1). then the algorithm's complexity is linearly proportional to source and nodes' edges." + ] + }, + { + "cell_type": "markdown", + "id": "ce61f522-c5b1-4b52-8c72-0571ffdf5965", + "metadata": {}, + "source": [ + "reflection:\n", + "(GPT cannot generate a similar answer by the way)\n", + "However, the method mentioned above is qutie informal, gpt offers a method which is more relative to what we have been taught(though it seems misunderstood the problem):\n", + "\n", + "Initialize the flow network G = (V, E) with unit capacity edges and costs ce = 1.\n", + "\n", + "Initialize a variable to keep track of the current flow F = 0.\n", + "\n", + " While k > 0:\n", + " a. Find the minimum cut in the network G between source s and target t. This can be done using algorithms like the Ford-Fulkerson method or the Edmonds-Karp algorithm for finding maximum flows.\n", + " b. Identify the minimum-capacity edge e* that crosses the minimum cut. This edge has the smallest contribution to the flow from s to t.\n", + " c. Delete the edge e* from the graph (i.e., remove it from E) and decrement k by 1.\n", + " d. Update the flow F by subtracting the capacity of e* from it.\n", + " Return the final flow F as the smallest possible flow from s to t after deleting k edges.\n", + "\n", + "This algorithm works by iteratively finding the minimum-capacity edge that contributes the least to the flow from s to t and removing it from the network. By doing this k times, you ensure that you remove k edges while minimizing the flow capacity from s to t.\n", + "The key step is finding the minimum cut efficiently, and well-known algorithms for finding maximum flows can be adapted for this purpose. These algorithms are typically polynomial-time.\n", + "The time complexity of this algorithm depends on the maximum number of iterations, which is bounded by k. The overall time complexity is therefore O(k * |V| * |E|), which is polynomial time if k is considered a constant.\n", + "\n", + "\n", + "The key part is to use Ford-Fulkerson method to detect maximum flow and delete other edges. It seems that gpt wanted to reserve flow as much as possible." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7f722c03-d282-4cf7-8f9a-318413c8b5b9", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "8917e376-7811-494a-acd6-7cd46a010b99", + "metadata": {}, + "source": [ + "Question6:\n", + "Network flow issues come up in dealing with natural disasters and other crises, since major unexpected events often require the movement and evacuation of large numbers of people in a short amount of time. Consider the following scenario. Due to large-scale flooding in a region, paramedics have identified a set of n injured people distributed across the region who need to be rushed to hospitals. There are k hospitals in the region, and each of the n people needs to be brought to a hospital that is within a halfhour’s driving time of their current location (so different people will have different options for hospitals, depending on where they are right now). \n", + "At the same time, one doesn’t want to overload any one of the hospitals by sending too many patients its way. The paramedics are in touch by cell phone, and they want to collectively work out whether they can choose a hospital for each of the injured people in such a way that the load on the hospitals is balanced: Each hospital receives at most [n/k] people. Give a polynomial-time algorithm that takes the given information about the people’s locations and determines whether this is possible. \n" + ] + }, + { + "cell_type": "markdown", + "id": "dfd3a50d-17b0-4b61-97ca-5071378af16e", + "metadata": {}, + "source": [ + "Solution:\n", + " Assumption1: n/k must be an integer so that all hospitals receive the exact number of patients, otherwise there must be a hospital which will receive patients more that n/k.\n", + " we can create a directed graph in which patients and hospitals can be represented as nodes. patient and hospitals are coonected by edge if patient can be brought to hospital within a halfhour’s driving time of their current location.\n", + " then we use a source connected to all the hospitals with capacity n/k, and patients connect to sink with capacity 1.\n", + " Then the condition only satisfies if there exists a s-t flow of value n. " + ] + }, + { + "cell_type": "markdown", + "id": "f78a1ee4-586d-4a92-ae57-7b0fd8b86e1e", + "metadata": {}, + "source": [ + "reflection: \n", + "Similar situation can be applied this method such as water resources should be evenly taken in several villages(Assume these resources will finnaly combined in downstream and people in downstream also need lot of water), different villages can only get the water from certain sources.\n", + "In this question: source and sink can switch positions, it only effects how we understand the diagrams. With more details, patients must connect to a final vertix with capacity 1, then it denotes that all patients would get help if the s-t flow has a value of n.\n", + "\n", + "\n", + "\n", + "\n", + "Surprisingly, gpt also get the exact right answer. Only make a few mistakes on the time comlpexity. Here is gpt's answer:\n", + "\n", + "Create a flow network with the following components:\n", + "Source node s.\n", + "Sink node t.\n", + "A node for each injured person, representing their current location.\n", + "A node for each hospital.\n", + "Connect the source node s to each injured person node with an edge with capacity 1 (representing the fact that each injured person needs to be assigned to a hospital).\n", + "Connect each hospital node to the sink node t with an edge with capacity [n/k] (representing the maximum number of people a hospital can receive).\n", + "Add edges from each injured person node to each hospital node with capacity 1 if the driving time from the injured person's location to the hospital is within half an hour. Otherwise, do not add this edge.\n", + "\n", + "The time complexity of this algorithm is polynomial, as it involves constructing a flow network with O(n) nodes and O(n*k) edges and then running a polynomial-time maximum flow algorithm." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a1d54e56-9766-4e15-9c41-217ef2521eed", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "76461ab4-4508-406c-9d56-0625c8b7811d", + "metadata": {}, + "source": [ + "Question 7: calculate the maximum sum of an array, but all the number involve this sumation can not be adjacent." + ] + }, + { + "cell_type": "markdown", + "id": "703fccae-35c4-463e-8264-ccb9add4f0ef", + "metadata": {}, + "source": [ + "solution:\n", + "persudo code\n", + " def max_sum(arr[i])\n", + " if i == -1 or i ==-2:\n", + " arr[i]=0\n", + " max_sum = max(max_sum(arr[i-1]), arr(i)+max_sum(arr[i-2]))\n", + " " + ] + }, + { + "cell_type": "markdown", + "id": "51c6983a-d9a2-4e18-a150-89690528de63", + "metadata": {}, + "source": [ + "reflection:\n", + " I understood this recurrance in this way: when a new number was added, we need to determine whether we need to add it. For sure we can add it to max_sum(arr[i-2]), then we need to compare it with max_sum(arr[i-1]). It also combines the circumstance that max_sum(arr[i-2])=max_sum(arr[i-1]).\n", + " \n", + "It seems that gpt doesn't understand the question well.\n", + "def max_sum_non_adjacent_recursive(arr, i):\n", + " if i == 0:\n", + " return arr[0]\n", + " if i == 1:\n", + " return max(arr[0], arr[1])\n", + " \n", + " # Calculate the maximum sum of non-adjacent elements\n", + " max_sum_i = max(arr[i] + max_sum_non_adjacent_recursive(arr, i - 2), max_sum_non_adjacent_recursive(arr, i - 1))\n", + " \n", + " return max_sum_i\n", + "\n", + "# Function to find the maximum sum of non-adjacent elements in an array\n", + "def find_max_sum_non_adjacent(arr):\n", + " n = len(arr)\n", + " if n == 0:\n", + " return 0\n", + " if n == 1:\n", + " return arr[0\n", + " \n", + " # Use the recurrence relation to find the maximum sum\n", + " max_sum = max(max_sum_non_adjacent_recursive(arr, n - 1), max_sum_non_adjacent_recursive(arr, n - 2))\n", + " \n", + "# Example usage:\n", + "arr = [2, 7, 9, 3, 4]\n", + "result = find_max_sum_non_adjacent(arr)\n", + "print(result) # Output: 10 (Non-adjacent elements: 2 and 8)\n", + "\n", + " " + ] + }, + { + "cell_type": "markdown", + "id": "822c938a-8171-4015-abed-c7ce32f0c7b9", + "metadata": {}, + "source": [ + "Question 8:\n", + "For each of the following recurrences, give an expression for the runtime T(n) if the recurrence can be \n", + "solved with the Master Theorem. Otherwise, indicate that the Master Theorem does not apply.\n", + "\n", + "A:T(n) = 16T(n/2)+ 10*n^3\n", + "B:T(n) = 2T(n/2)+ 7*n\n", + "C:T(n) = 2^n*T(n/4) + n*log n\n", + "D:T(n) = T(n/3) + T(n/4) + n" + ] + }, + { + "cell_type": "markdown", + "id": "24a7f551-1af4-48b5-8305-82cfca2baaa3", + "metadata": {}, + "source": [ + "Solution:\n", + " \n", + "A:\n", + "log(b)a=4 c=3, log(b)a>c\n", + "\n", + "T(n)=(n^log(b)a)=n^4\n", + "\n", + "\n", + "B:log(b)a=1, k=0 n^(log(b)a)*(logn^k)=n=O(f(n))\n", + "T(n)=(n*logn)\n", + "\n", + "C:a is a variable of n so it's not applicable \n", + "\n", + "D:it's not applicable since it's not follow the format of master theorem\n" + ] + }, + { + "cell_type": "markdown", + "id": "3343f809-94f6-448f-9a43-81a9ad0685bc", + "metadata": {}, + "source": [ + "Reflection:\n", + "It's a question focusing on the master therom. We can solve it just based on the fomula." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5f43eb8f-1a83-487d-8bc7-6c99f569e1b1", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "56e55887-3114-4602-b160-9277a685c39b", + "metadata": {}, + "source": [ + "Question 9:\n", + "Use dynamic programming to solve knapsack problem:\n", + "values = [10, 40, 60, 45]\n", + "weights = [4, 3, 2, 1]\n", + "knapsack_capacity = 5\n", + "\n", + "Solution:\n", + "value weight Maxweights:\n", + " 0 1 2 3 4 5 \n", + "0 0 0 0 0 0 0 0\n", + "10 4 0 0 0 0 10 10\n", + "40 3 0 \n", + "60 2 0\n", + "45 1 0\n", + "\n", + "\n", + " 0 1 2 3 4 5 \n", + "0 0 0 0 0 0 0 0\n", + "10 4 0 0 0 0 10 10\n", + "40 3 0 0 0 0 40 40\n", + "60 2 0\n", + "45 1 0\n", + "\n", + "\n", + " 0 1 2 3 4 5 \n", + "0 0 0 0 0 0 0 0\n", + "10 4 0 0 0 0 10 10\n", + "40 3 0 0 0 40 40 40\n", + "60 2 0\n", + "45 1 0\n", + "\n", + "\n", + " 0 1 2 3 4 5 \n", + "0 0 0 0 0 0 0 0\n", + "10 4 0 0 0 0 10 10\n", + "40 3 0 0 0 40 40 40\n", + "60 2 0 0 60 60 60 100\n", + "45 1 0\n", + "\n", + " 0 1 2 3 4 5 \n", + "0 0 0 0 0 0 0 0\n", + "10 4 0 0 0 0 10 10\n", + "40 3 0 0 0 40 40 40\n", + "60 2 0 0 60 60 60 100\n", + "45 1 0 45 60 105 105 105\n", + "\n", + "\n", + "\n", + "The combination is (1,2)" + ] + }, + { + "cell_type": "markdown", + "id": "5915aa0d-ad16-4363-8f41-70a602734baf", + "metadata": {}, + "source": [ + "Reflection:\n", + "The gpt gave me some codes:\n", + "\n", + "\n", + "'''\n", + "def knapsack_01(values, weights, capacity):\n", + " n = len(values)\n", + " dp = [[0 for _ in range(capacity + 1)] for _ in range(n + 1)]\n", + "\n", + " for i in range(1, n + 1):\n", + " for w in range(1, capacity + 1):\n", + " if weights[i - 1] <= w:\n", + " dp[i][w] = max(dp[i - 1][w], dp[i - 1][w - weights[i - 1]] + values[i - 1])\n", + " else:\n", + " dp[i][w] = dp[i - 1][w]\n", + "\n", + " # Find the selected items\n", + " selected_items = []\n", + " i, j = n, capacity\n", + " while i > 0 and j > 0:\n", + " if dp[i][j] != dp[i - 1][j]:\n", + " selected_items.append(i - 1)\n", + " j -= weights[i - 1]\n", + " i -= 1\n", + "\n", + " selected_items.reverse()\n", + " return dp[n][capacity], selected_items\n", + "'''\n", + "This algorithm is really beautiful, and so is the code.\n", + "It shows how we can break a problem into tiny small parts, then using these little parts to build up a perfect answer." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Submissions/002771818_Yiheng_Ye/Assignment4.ipynb b/Submissions/002771818_Yiheng_Ye/Assignment4.ipynb new file mode 100644 index 0000000..6bb67f7 --- /dev/null +++ b/Submissions/002771818_Yiheng_Ye/Assignment4.ipynb @@ -0,0 +1,263 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "026d9675-4bf3-4a5b-a9d1-089f074cc89d", + "metadata": {}, + "source": [ + "Question1:\n", + "Max Clique question:\n", + " A complete subgraph of a graph G is called a clique, whose size is the number of vertices in it. Find the maximal clique in a given graph G.\n", + " Is this problem NP?\n", + " Is this problem NPC?" + ] + }, + { + "cell_type": "markdown", + "id": "97490f04-5a57-44b5-8fcc-ffe499b955a4", + "metadata": {}, + "source": [ + "Solution:\n", + " To show it is NP:\n", + " By checking the connectivity of each vertice, the time complexty will be O(n^2), all we need to do is to check the vertice one by one, and the edge we need to check is the summation of series of number of edges n. So this problem is a NP problem.\n", + " \n", + " To show it is NP-C:\n", + " we can use CNF-SAT to solve this problem. Firstly, we can create a function K= C1*C2*C3*....*Ck. K is a CNF-3 with k clauses. Each clause has three variables x1 x2 x3. for example C1= x1*-x2*x3 in the formula\n", + " A graph can be constructed as follows: for each C, creates a triple of vertices v1,v2,v3 and create edges between vi(v from Ci) and vj iff:\n", + " *they are in different triples,i is not equal to j\n", + " *they do not correspond to the literals negating each other.\n", + " \n", + " For example: K=(x1*-x2*-x3)*(-x1*x2*x3)*(x1*x2*x3)\n", + " we can have x1=1 x2=0 x3=1\n", + " then for corresponding clique, we can pick true literal for each triple." + ] + }, + { + "cell_type": "markdown", + "id": "7de7e366-f91b-4ebc-bfb6-a791a1ed1e67", + "metadata": {}, + "source": [ + "reflection:\n", + " This question tests the understanding of NPC-related question and the usage of the known NPC problems.\n", + " Gpt suggests using boolean SAT to solve this problem, which is kind of similar.\n", + " \n", + " Gpt:\n", + " One classic NP-complete problem is the Boolean Satisfiability Problem (SAT). We can reduce SAT to the maximum clique problem by constructing a graph from a given instance of SAT.\n", + "Given a Boolean formula in conjunctive normal form (CNF) with variables x1,x2..xn, we create a graph where each variable corresponds to a node and each clause corresponds to an edge between the nodes.\n", + "\n", + "For each clause in the Boolean formula, create an edge between the nodes representing the literals in the clause. This graph will have an edge between two nodes (variables) if they appear together in a clause.\n", + "\n", + "The maximum clique in this constructed graph will correspond to a satisfying assignment for the original SAT problem. If there exists a clique of size k (where k is the number of variables in a satisfying assignment), it means that those variables satisfy the Boolean formula. Conversely, if there is no such clique, the Boolean formula is unsatisfiable.\n", + " \n", + " \n", + " It was hard to think of this quesion at first. I took lots of time on thinking and searching, with the hint of SAT given by gpt, i finally got something on this question. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cda160dc-c4b3-4939-b481-685aea564d8d", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "41813994-e49e-4c7d-9001-0070a3b05c38", + "metadata": {}, + "source": [ + "Question2:\n", + " Given a list of cities and the distances between each pair of cities, find the shortest possible route that visits each city exactly once and returns to the origin city.\n", + " Is this problem NP?\n", + " Is this problem NPC?\n", + " Is this problem NPH?" + ] + }, + { + "cell_type": "markdown", + "id": "547af91a-cc93-46d8-aae6-6103f90a8180", + "metadata": {}, + "source": [ + "Solution:\n", + " Since we do not know what is the exact number of the shortest path. We can change the decision question into \" given a cost C, is there a route that's cheaper than C? \"\n", + " \n", + " It's hard to find a similar NPC problem which is able to be used in polynomial reduction.\n", + " \n", + " We are able to prove it NPH through the Hamiltonian cycle problem.\n", + " Hamiltonian Cycle problem consists of a graph G =(V, E)\n", + " Travelling Salesman problem consisting of graph G’ = (V’, E’) and the maximum cost, K. \n", + " Here is the following tranformation For all the edges e belonging to E, add the cost of edge c(e)=1. Connect the remaining edges, e’ belonging to E’, that are not present in the original graph G, each with a cost c(e’)= 2.\n", + "And, set K = N.\n", + " Let's imagine a scenario where a graph G has a Hamiltonian Cycle, encompassing all its vertices V. These vertices then create a Traveling Salesman Problem (TSP) with a cost = N. This arises because it utilizes all the edges in the initial graph, each having a cost c(e) = 1. Furthermore, as it forms a cycle, it naturally returns to the starting vertex.\n", + "\n", + "Now, consider another graph G’ containing a TSP with a cost, K = N. This TSP traverses every vertex in the graph and returns to the starting point. As none of the vertices are excluded from the graph and the total cost equals n, it implies all edges in E, each having a cost of 1. Consequently, this forms a Hamiltonian cycle within graph G.\n", + "\n", + "Hence, if graph G holds a Hamiltonian Cycle, then graph G’ holds a TSP. Consequently, every case of the Traveling Salesman Problem can be transformed into a Hamiltonian Cycle problem. Therefor, the TSP is NP-Hard." + ] + }, + { + "cell_type": "markdown", + "id": "f976dc7c-83e3-46a8-899c-538e7f0e88e8", + "metadata": {}, + "source": [ + "Reflection:\n", + " It costs me lots of time to think how to demonstrate that finding the min value of TSP is a NP problem. GPT says to build the treshold then it can be.\n", + " But the point confuses me is that how can i know the threshold if I didn't find the min. It comes to me later that the problem becomes to a yes or no question after building the threshold, how large is it doesn't matter.\n", + " \n", + " Gpt did give the hint to use Hamiltonian cycle, so the process is kind of nature.\n", + " However, Gpt still thinks TSP is NPC regradless of the transformation time. also making a mistake like\"Therefore, since the Traveling Salesman Problem (TSP) is in NP and is NP-hard (by reduction from the Hamiltonian cycle problem), it is NP-complete.\"\n", + " \n", + " Btw, the key part of this solution is understanding of Hamiltonian cycle." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8ae108c3-a32e-456e-8eb6-af423a59c35a", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "65fb75f4-a795-4b4f-b2ae-c419343354f3", + "metadata": {}, + "source": [ + "Question3:\n", + " Show the knapsack problem is NPC.\n", + " " + ] + }, + { + "cell_type": "markdown", + "id": "2a1393d9-9199-4d23-846a-e975e863ab65", + "metadata": {}, + "source": [ + "Solution:\n", + " To show it is NP, the optimal question can be changed into:\n", + " Is the values in the solution larger than V?\n", + " Is the weights in the solution larger than W?\n", + " The reduction can also be easily told in this part, this is just like two subset-sum problem. By the boundness of polynomial, the solution of two subset-sum question will still be polynomial." + ] + }, + { + "cell_type": "markdown", + "id": "66c46dc8-f119-4721-a426-1cf14a375d9b", + "metadata": {}, + "source": [ + "Reflection:\n", + " Gpt gave me this question, though it seems Knapsack problem is not NPC at first, but after researching and thinking, the result is interesting.\n", + " It's shown in my gpt that knapsack problem is not NPC, \"So, while the classical 0/1 Knapsack Problem isn't NP-complete, certain extensions or variations under specific conditions can fall into the NP-complete category.\", gpt said so.\n", + " \n", + " knapsack problem is pseudo-polynomial,The dynamic programming solution is indeed linear in the value of W, but exponential in the length of W — and that's what matters. In other words, the algorithm goes much slower when W gets too large." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d9760ea8-fb93-49a0-9214-dd3f07d587d2", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "f208678e-2e05-4936-a25f-17044eac7e16", + "metadata": {}, + "source": [ + "Question4:\n", + " Coloring problem:Given an undirected graph G = (V, E), assign a color to each vertex in V using k colors so that no two adjacent vertices have the same color.\n", + " Is it NP?\n", + " Is it NPC?\n", + " " + ] + }, + { + "cell_type": "markdown", + "id": "0ce6ad4e-806b-4e84-a1a8-30f0530233e4", + "metadata": {}, + "source": [ + " It is NP by checking the adjacent vertices in graph one by one in the given solution, we just need to loop in the whole solution. So the coloring problem is in NP.\n", + " Also, it is a NP complete , by reduction from Edge coloring.Edge coloring is a well-known NP-hard problem, and hence there is Karp reduction from every problem in NP to it. In particular there must also be a reduction from vertex coloring (which is NP-hard) to edge coloring. The reduction from edge coloring to vertex coloring creates a vertex Ve for every edge e in G and an edge (Ve,Vf)for every pair of distinct edges e and fin G that share an endpoint." + ] + }, + { + "cell_type": "markdown", + "id": "5cbd58a5-2920-41ff-a17b-b24fa0d9d3c8", + "metadata": {}, + "source": [ + "Reflection:\n", + " edge coloring is reduction from 3-SAT problem, thus forms a reduction chain.\n", + " Gpt also metnioned 3-SAT but not detaily how to use that." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3a486d41-94d3-46c5-bc65-cae38f66900e", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "70c08457-c451-40c5-bece-46ed51eb76d4", + "metadata": {}, + "source": [ + "Question5:\n", + " Nested subset-sum problem:\n", + " In a set of integers S, do we have a subset s1 whose sum equals to x, and do we have a sub set s2 whose sum equals to x2?\n", + " show that it is NPC.\n", + " " + ] + }, + { + "cell_type": "markdown", + "id": "20c8b307-580f-466a-b292-a4da9ccc5ee4", + "metadata": {}, + "source": [ + "Solution:\n", + " To show it it NP:\n", + " make summation of the given subsets and check obviously polynomial.\n", + " To show it is NPC:\n", + " According to the class material, we know that subset sum is NPC.\n", + " In reduction process, we can make y=x, then whatever the inputs are, we could get the same answer in the decision algorithm. the transforamtion is clearly linear time, so Nested subset-sum problem is still NPC.\n", + " " + ] + }, + { + "cell_type": "markdown", + "id": "bda1f392-f3af-47a4-8ec6-9b6ab1f78379", + "metadata": {}, + "source": [ + "Refelction:\n", + " This is a very intersting question,I know Nested subset-sum problem is NPC form instinct but stuck a little on how to prove it. The nested loop making me think about how to use the input to indicate the \"nested\" part and ignore that I can use a more direct way to solve it(make y=x)\n", + " \n", + " GPT did gave the same answer as the solution.\n", + " \n", + " In other perspective, since Nested subset-sum problem only takes one more nested step compared to the old one, we can recogonize that the polynomial bounds didn't change(since it seems like a multiplication). but this thought is not precise and clear enough to be a prove." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Submissions/002771818_Yiheng_Ye/readme.md b/Submissions/002771818_Yiheng_Ye/readme.md index 1c8405d..7b525b6 100644 --- a/Submissions/002771818_Yiheng_Ye/readme.md +++ b/Submissions/002771818_Yiheng_Ye/readme.md @@ -2,5 +2,7 @@ First Name: "Yiheng" Last Name : "Ye" NU_ID : "002771818" -Assignment-1 : "Summary of assignment 1" + +Assignment : "Each assignment and its summarry is contained in the ipyb file" +