Skip to content

Instantly share code, notes, and snippets.

@ekzhang
Last active October 7, 2021 13:24
Show Gist options
  • Save ekzhang/98804315e84f43505630288a7d25530d to your computer and use it in GitHub Desktop.
Save ekzhang/98804315e84f43505630288a7d25530d to your computer and use it in GitHub Desktop.
A very short solution to Problem N (https://judge.icpc.global/problems/vector) from ICPC Moscow WF in Oct. 2021
### A Pluto.jl notebook ###
# v0.16.1
using Markdown
using InteractiveUtils
# ╔═╡ 4149ed32-773a-48e8-a871-879d36d441f1
using LinearAlgebra
# ╔═╡ 5c627248-2663-11ec-0f78-a773b4888ba6
using Distributions
# ╔═╡ ac8c0662-8a66-42f5-b6ef-824b060ed77e
md"""
# ICPC Moscow WF - Problem N
This is a very short solution (<20 lines of code) with test harness in Julia.
The format of this file is a [Pluto.jl](https://github.com/fonsp/Pluto.jl) notebook, which is a tool for interactive computation. If you change any cell, all cells that depend it will be automatically rerun.
"""
# ╔═╡ 6ab01721-a487-4694-a84a-de742dd232bf
md"""
## Data Generation
First, let's define a helper function that generates random input data according to the random process generating the input. Note that we will additionally assume that $n \leq d + 1$ here.
(In the original problem, you can get this assumption for free by setting $n = \min(n, d + 1)$ at the beginning of the program and ignoring all other input data points.)
"""
# ╔═╡ 40eb816d-6f39-42e1-a2ba-1e027c06f138
dist = Uniform(-100.0, 100.0)
# ╔═╡ bfd83aa6-6b1b-4b83-a7b9-e9dd525de179
function gen_input(d::Int, n::Int)
points = rand(dist, d, n)
target = rand(dist, d)
dists = norm.(eachcol(points .- target))
points, dists
end
# ╔═╡ bb51a417-8c34-4b84-8f82-1ab27bd81da9
# The largest input from the problem is `gen_input(500, 500)`.
# For this input, the notebook runs in 16 ms on my laptop.
points, dists = gen_input(4, 4)
# ╔═╡ 1312f4c3-4b69-4601-9528-f625ebe85aa9
md"""
## Solving the Problem
Assume that $n \geq 2$, as otherwise, the problem is trivial. Assume without loss of generality that the first point `base = points[0]` is at the origin; we shift all the other points so that this is the case.
If our point is $x \in \mathbb R^d$, the problem reduces to solving the following constraints:
$$\begin{aligned}
\| x \|^2 &= d_1^2, \\
\| x - p_i \|^2 &= d_i^2, \quad \text{for } i = 2, 3, \ldots, n.
\end{aligned}$$
Unfortunately, these are quadratic constraints. We can simplify using the identity $\|x-p_i\|^2 = (x-p_i) \cdot (x-p_i) = \|x\|^2 - 2x\cdot p_i + \|p_i\|^2$ to get:
$$\begin{aligned}
\| x \|^2 &= d_1^2, \\
2 p_i \cdot x &= \|p\|^2 - d_i^2 + d_1^2, \quad \text{for } i = 2, 3, \ldots, n.
\end{aligned}$$
The latter constraints are simply a linear equation, so we can write them in the form $Ax = b$, for $A \in \mathbb R^{(n - 1) \times d}$ and $b \in \mathbb R^{n-1}$.
"""
# ╔═╡ 2c36c6ed-13d0-4959-aa27-5012fd6acefc
base, rest = points[:, 1], points[:, 2:end] .- points[:, 1]
# ╔═╡ 9d247585-bf43-4a28-b1d1-c9781fcb6b52
A = 2 .* rest'
# ╔═╡ 5f0b1a02-8e8b-4c31-822f-5612a2b7250a
b = [dot(p, p) - dists[i + 1]^2 + dists[1]^2 for (i, p) = enumerate(eachcol(rest))]
# ╔═╡ 1749cf2a-feac-4b3a-b8e8-de4fab87a50c
md"""
To finish the problem, we compute the least-norm solution to $Ax = b$ using QR decomposition. In contest, we implemented this using a simple Gram-Schmidt orthonormalization algorithm in C++.
Since it is the least-norm solution, we are guaranteed to have $\|x\|^2 \leq d_1^2$. Finally, it suffices to find an arbitrary unit vector $o$ in the kernel of $A$ and add $o \sqrt{d_1^2 - \|x\|^2}$ to the least-norm solution $x$.
"""
# ╔═╡ 2883109e-908c-4956-b6ed-cff2267f1966
# A = R'Q'
Q, R = qr(A')
# ╔═╡ a8b93dbb-23c0-4d39-b59b-e5dd2dffb553
# R'Q'x = b
begin
local value = R' \ b
while length(value) < size(Q)[1]
push!(value, 0.0)
end
x = Q * value
if norm(x) < dists[1]
local o = zeros(length(value))
o[end] = sqrt(dists[1]^2 - norm(x)^2)
x = Q * (value + o)
end
@assert isapprox(norm(x), dists[1])
x += base
end
# ╔═╡ 9b63ad91-705a-4695-8ad8-f1313c6c9e3f
md"""
## Validation
Let's check that we solved our input correctly!
You can change the values of $n$ and $d$ in the first cell to verify that this solution still works, as well as to see the runtime of each code block.
"""
# ╔═╡ c1216646-5cf4-4c05-9cc1-d3cd331eca46
# grader: verify conditions
begin
for (p, d) = zip(eachcol(points), dists)
@assert isapprox(norm(x - p), d)
end
"Tests passed!"
end
# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
[compat]
Distributions = "~0.25.18"
"""
# ╔═╡ 00000000-0000-0000-0000-000000000002
PLUTO_MANIFEST_TOML_CONTENTS = """
# This file is machine-generated - editing it directly is not advised
[[Artifacts]]
deps = ["Pkg"]
git-tree-sha1 = "c30985d8821e0cd73870b17b0ed0ce6dc44cb744"
uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"
version = "1.3.0"
[[Base64]]
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
[[ChainRulesCore]]
deps = ["Compat", "LinearAlgebra", "SparseArrays"]
git-tree-sha1 = "a325370b9dd0e6bf5656a6f1a7ae80755f8ccc46"
uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
version = "1.7.2"
[[Compat]]
deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"]
git-tree-sha1 = "31d0151f5716b655421d9d75b7fa74cc4e744df2"
uuid = "34da2185-b29b-5c13-b0c7-acf172513d20"
version = "3.39.0"
[[CompilerSupportLibraries_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "8e695f735fca77e9708e795eda62afdb869cbb70"
uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae"
version = "0.3.4+0"
[[DataAPI]]
git-tree-sha1 = "cc70b17275652eb47bc9e5f81635981f13cea5c8"
uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
version = "1.9.0"
[[DataStructures]]
deps = ["Compat", "InteractiveUtils", "OrderedCollections"]
git-tree-sha1 = "7d9d316f04214f7efdbb6398d545446e246eff02"
uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
version = "0.18.10"
[[Dates]]
deps = ["Printf"]
uuid = "ade2ca70-3891-5945-98fb-dc099432e06a"
[[DelimitedFiles]]
deps = ["Mmap"]
uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab"
[[Distributed]]
deps = ["Random", "Serialization", "Sockets"]
uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b"
[[Distributions]]
deps = ["ChainRulesCore", "FillArrays", "LinearAlgebra", "PDMats", "Printf", "QuadGK", "Random", "SparseArrays", "SpecialFunctions", "Statistics", "StatsBase", "StatsFuns"]
git-tree-sha1 = "ff7890c74e2eaffbc0b3741811e3816e64b6343d"
uuid = "31c24e10-a181-5473-b8eb-7969acd0382f"
version = "0.25.18"
[[DocStringExtensions]]
deps = ["LibGit2"]
git-tree-sha1 = "a32185f5428d3986f47c2ab78b1f216d5e6cc96f"
uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
version = "0.8.5"
[[FillArrays]]
deps = ["LinearAlgebra", "Random", "SparseArrays", "Statistics"]
git-tree-sha1 = "29890dfbc427afa59598b8cfcc10034719bd7744"
uuid = "1a297f60-69ca-5386-bcde-b61e274b549b"
version = "0.12.6"
[[InteractiveUtils]]
deps = ["Markdown"]
uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
[[IrrationalConstants]]
git-tree-sha1 = "f76424439413893a832026ca355fe273e93bce94"
uuid = "92d709cd-6900-40b7-9082-c6be49f344b6"
version = "0.1.0"
[[JLLWrappers]]
deps = ["Preferences"]
git-tree-sha1 = "642a199af8b68253517b80bd3bfd17eb4e84df6e"
uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210"
version = "1.3.0"
[[LibGit2]]
deps = ["Printf"]
uuid = "76f85450-5226-5b5a-8eaa-529ad045b433"
[[Libdl]]
uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
[[LinearAlgebra]]
deps = ["Libdl"]
uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
[[LogExpFunctions]]
deps = ["ChainRulesCore", "DocStringExtensions", "IrrationalConstants", "LinearAlgebra"]
git-tree-sha1 = "34dc30f868e368f8a17b728a1238f3fcda43931a"
uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688"
version = "0.3.3"
[[Logging]]
uuid = "56ddb016-857b-54e1-b83d-db4d58db5568"
[[Markdown]]
deps = ["Base64"]
uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"
[[Missings]]
deps = ["DataAPI"]
git-tree-sha1 = "bf210ce90b6c9eed32d25dbcae1ebc565df2687f"
uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28"
version = "1.0.2"
[[Mmap]]
uuid = "a63ad114-7e13-5084-954f-fe012c677804"
[[OpenLibm_jll]]
deps = ["Libdl", "Pkg"]
git-tree-sha1 = "d22054f66695fe580009c09e765175cbf7f13031"
uuid = "05823500-19ac-5b8b-9628-191a04bc5112"
version = "0.7.1+0"
[[OpenSpecFun_jll]]
deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "9db77584158d0ab52307f8c04f8e7c08ca76b5b3"
uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e"
version = "0.5.3+4"
[[OrderedCollections]]
git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c"
uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
version = "1.4.1"
[[PDMats]]
deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse"]
git-tree-sha1 = "4dd403333bcf0909341cfe57ec115152f937d7d8"
uuid = "90014a1f-27ba-587c-ab20-58faa44d9150"
version = "0.11.1"
[[Pkg]]
deps = ["Dates", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "UUIDs"]
uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
[[Preferences]]
deps = ["TOML"]
git-tree-sha1 = "00cfd92944ca9c760982747e9a1d0d5d86ab1e5a"
uuid = "21216c6a-2e73-6563-6e65-726566657250"
version = "1.2.2"
[[Printf]]
deps = ["Unicode"]
uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7"
[[QuadGK]]
deps = ["DataStructures", "LinearAlgebra"]
git-tree-sha1 = "78aadffb3efd2155af139781b8a8df1ef279ea39"
uuid = "1fd47b50-473d-5c70-9696-f719f8f3bcdc"
version = "2.4.2"
[[REPL]]
deps = ["InteractiveUtils", "Markdown", "Sockets"]
uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
[[Random]]
deps = ["Serialization"]
uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
[[Reexport]]
git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b"
uuid = "189a3867-3050-52da-a836-e630ba90ab69"
version = "1.2.2"
[[Rmath]]
deps = ["Random", "Rmath_jll"]
git-tree-sha1 = "86c5647b565873641538d8f812c04e4c9dbeb370"
uuid = "79098fc4-a85e-5d69-aa6a-4863f24498fa"
version = "0.6.1"
[[Rmath_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "1b7bf41258f6c5c9c31df8c1ba34c1fc88674957"
uuid = "f50d1b31-88e8-58de-be2c-1cc44531875f"
version = "0.2.2+2"
[[SHA]]
uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce"
[[Serialization]]
uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
[[SharedArrays]]
deps = ["Distributed", "Mmap", "Random", "Serialization"]
uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383"
[[Sockets]]
uuid = "6462fe0b-24de-5631-8697-dd941f90decc"
[[SortingAlgorithms]]
deps = ["DataStructures"]
git-tree-sha1 = "b3363d7460f7d098ca0912c69b082f75625d7508"
uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c"
version = "1.0.1"
[[SparseArrays]]
deps = ["LinearAlgebra", "Random"]
uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
[[SpecialFunctions]]
deps = ["ChainRulesCore", "IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"]
git-tree-sha1 = "793793f1df98e3d7d554b65a107e9c9a6399a6ed"
uuid = "276daf66-3868-5448-9aa4-cd146d93841b"
version = "1.7.0"
[[Statistics]]
deps = ["LinearAlgebra", "SparseArrays"]
uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
[[StatsAPI]]
git-tree-sha1 = "1958272568dc176a1d881acb797beb909c785510"
uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0"
version = "1.0.0"
[[StatsBase]]
deps = ["DataAPI", "DataStructures", "LinearAlgebra", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"]
git-tree-sha1 = "8cbbc098554648c84f79a463c9ff0fd277144b6c"
uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
version = "0.33.10"
[[StatsFuns]]
deps = ["ChainRulesCore", "IrrationalConstants", "LogExpFunctions", "Reexport", "Rmath", "SpecialFunctions"]
git-tree-sha1 = "95072ef1a22b057b1e80f73c2a89ad238ae4cfff"
uuid = "4c63d2b9-4356-54db-8cca-17b64c39e42c"
version = "0.9.12"
[[SuiteSparse]]
deps = ["Libdl", "LinearAlgebra", "Serialization", "SparseArrays"]
uuid = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9"
[[TOML]]
deps = ["Dates"]
git-tree-sha1 = "44aaac2d2aec4a850302f9aa69127c74f0c3787e"
uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
version = "1.0.3"
[[Test]]
deps = ["Distributed", "InteractiveUtils", "Logging", "Random"]
uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
[[UUIDs]]
deps = ["Random", "SHA"]
uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
[[Unicode]]
uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"
"""
# ╔═╡ Cell order:
# ╟─ac8c0662-8a66-42f5-b6ef-824b060ed77e
# ╠═4149ed32-773a-48e8-a871-879d36d441f1
# ╠═5c627248-2663-11ec-0f78-a773b4888ba6
# ╟─6ab01721-a487-4694-a84a-de742dd232bf
# ╠═40eb816d-6f39-42e1-a2ba-1e027c06f138
# ╠═bfd83aa6-6b1b-4b83-a7b9-e9dd525de179
# ╠═bb51a417-8c34-4b84-8f82-1ab27bd81da9
# ╟─1312f4c3-4b69-4601-9528-f625ebe85aa9
# ╠═2c36c6ed-13d0-4959-aa27-5012fd6acefc
# ╠═9d247585-bf43-4a28-b1d1-c9781fcb6b52
# ╠═5f0b1a02-8e8b-4c31-822f-5612a2b7250a
# ╟─1749cf2a-feac-4b3a-b8e8-de4fab87a50c
# ╠═2883109e-908c-4956-b6ed-cff2267f1966
# ╠═a8b93dbb-23c0-4d39-b59b-e5dd2dffb553
# ╟─9b63ad91-705a-4695-8ad8-f1313c6c9e3f
# ╠═c1216646-5cf4-4c05-9cc1-d3cd331eca46
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002
Display the source blob
Display the rendered blob
Raw
using LinearAlgebra
# Read input
d, n = [parse(Int, x) for x = split(readline())]
n = min(n, d + 1)
data = hcat([[parse(Float64, x) for x = split(readline())] for _ = 1:n]...)
points, dists = data[1:end - 1, :], data[end, :]
# Shift first point to the origin
base, rest = points[:, 1], points[:, 2:end] .- points[:, 1]
# Edge case: n = 1
if n == 1 (base[end] += dists[1]; println(join(base, " ")); exit(0)) end
# Compute linear equation coefficients
A = 2 .* rest'
b = [norm(p)^2 - d^2 + dists[1]^2 for (p, d) = zip(eachcol(rest), dists[2:end])]
# Least-norm solution
Q, R = qr(A')
value = vcat(R' \ b, zeros(d - (n - 1)))
# Shift so that the norm equals dists[1]
value[end] += sqrt(max(0, dists[1]^2 - norm(value)^2))
# Output answer
println(join(Q * value + base, " "))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment