From 1610d6caf3903555f53badd9181562b45bdc39a6 Mon Sep 17 00:00:00 2001 From: Danylo Negrienko Date: Mon, 1 Jul 2024 12:38:21 -0400 Subject: [PATCH] binary_search --- elixir/binary-search/.exercism/config.json | 31 ++++++++ elixir/binary-search/.exercism/metadata.json | 1 + elixir/binary-search/.formatter.exs | 4 + elixir/binary-search/.gitignore | 24 ++++++ elixir/binary-search/HELP.md | 75 +++++++++++++++++++ elixir/binary-search/README.md | 71 ++++++++++++++++++ elixir/binary-search/lib/binary_search.ex | 34 +++++++++ elixir/binary-search/mix.exs | 28 +++++++ .../binary-search/test/binary_search_test.exs | 57 ++++++++++++++ elixir/binary-search/test/test_helper.exs | 2 + 10 files changed, 327 insertions(+) create mode 100644 elixir/binary-search/.exercism/config.json create mode 100644 elixir/binary-search/.exercism/metadata.json create mode 100644 elixir/binary-search/.formatter.exs create mode 100644 elixir/binary-search/.gitignore create mode 100644 elixir/binary-search/HELP.md create mode 100644 elixir/binary-search/README.md create mode 100644 elixir/binary-search/lib/binary_search.ex create mode 100644 elixir/binary-search/mix.exs create mode 100644 elixir/binary-search/test/binary_search_test.exs create mode 100644 elixir/binary-search/test/test_helper.exs diff --git a/elixir/binary-search/.exercism/config.json b/elixir/binary-search/.exercism/config.json new file mode 100644 index 0000000..944abbb --- /dev/null +++ b/elixir/binary-search/.exercism/config.json @@ -0,0 +1,31 @@ +{ + "authors": [ + "bernardoamc" + ], + "contributors": [ + "angelikatyborska", + "Cohen-Carlisle", + "devonestes", + "lpil", + "martinsvalin", + "neenjaw", + "parkerl", + "screamingjungle", + "sotojuan", + "waiting-for-dev" + ], + "files": { + "solution": [ + "lib/binary_search.ex" + ], + "test": [ + "test/binary_search_test.exs" + ], + "example": [ + ".meta/example.ex" + ] + }, + "blurb": "Implement a binary search algorithm.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Binary_search_algorithm" +} diff --git a/elixir/binary-search/.exercism/metadata.json b/elixir/binary-search/.exercism/metadata.json new file mode 100644 index 0000000..a203d1a --- /dev/null +++ b/elixir/binary-search/.exercism/metadata.json @@ -0,0 +1 @@ +{"track":"elixir","exercise":"binary-search","id":"46907374d6fd43e0815717c10201d2e0","url":"https://exercism.org/tracks/elixir/exercises/binary-search","handle":"negrienko","is_requester":true,"auto_approve":false} \ No newline at end of file diff --git a/elixir/binary-search/.formatter.exs b/elixir/binary-search/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/elixir/binary-search/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/elixir/binary-search/.gitignore b/elixir/binary-search/.gitignore new file mode 100644 index 0000000..75ae0f4 --- /dev/null +++ b/elixir/binary-search/.gitignore @@ -0,0 +1,24 @@ +# The directory Mix will write compiled artifacts to. +/_build/ + +# If you run "mix test --cover", coverage assets end up here. +/cover/ + +# The directory Mix downloads your dependencies sources to. +/deps/ + +# Where third-party dependencies like ExDoc output generated docs. +/doc/ + +# Ignore .fetch files in case you like to edit your project deps locally. +/.fetch + +# If the VM crashes, it generates a dump, let's ignore it too. +erl_crash.dump + +# Also ignore archive artifacts (built via "mix archive.build"). +*.ez + +# Ignore package tarball (built via "mix hex.build"). +binary_search-*.tar + diff --git a/elixir/binary-search/HELP.md b/elixir/binary-search/HELP.md new file mode 100644 index 0000000..0849902 --- /dev/null +++ b/elixir/binary-search/HELP.md @@ -0,0 +1,75 @@ +# Help + +## Running the tests + +From the terminal, change to the base directory of the exercise then execute the tests with: + +```bash +$ mix test +``` + +This will execute the test file found in the `test` subfolder -- a file ending in `_test.exs` + +Documentation: + +* [`mix test` - Elixir's test execution tool](https://hexdocs.pm/mix/Mix.Tasks.Test.html) +* [`ExUnit` - Elixir's unit test library](https://hexdocs.pm/ex_unit/ExUnit.html) + +## Pending tests + +In test suites of practice exercises, all but the first test have been tagged to be skipped. + +Once you get a test passing, you can unskip the next one by commenting out the relevant `@tag :pending` with a `#` symbol. + +For example: + +```elixir +# @tag :pending +test "shouting" do + assert Bob.hey("WATCH OUT!") == "Whoa, chill out!" +end +``` + +If you wish to run all tests at once, you can include all skipped test by using the `--include` flag on the `mix test` command: + +```bash +$ mix test --include pending +``` + +Or, you can enable all the tests by commenting out the `ExUnit.configure` line in the file `test/test_helper.exs`. + +```elixir +# ExUnit.configure(exclude: :pending, trace: true) +``` + +## Useful `mix test` options + +* `test/.exs:LINENUM` - runs only a single test, the test from `.exs` whose definition is on line `LINENUM` +* `--failed` - runs only tests that failed the last time they ran +* `--max-failures` - the suite stops evaluating tests when this number of test failures +is reached +* `--seed 0` - disables randomization so the tests in a single file will always be ran +in the same order they were defined in + +## Submitting your solution + +You can submit your solution using the `exercism submit lib/binary_search.ex` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Elixir track's documentation](https://exercism.org/docs/tracks/elixir) +- The [Elixir track's programming category on the forum](https://forum.exercism.org/c/programming/elixir) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +If you're stuck on something, it may help to look at some of the [available resources](https://exercism.org/docs/tracks/elixir/resources) out there where answers might be found. \ No newline at end of file diff --git a/elixir/binary-search/README.md b/elixir/binary-search/README.md new file mode 100644 index 0000000..9a9faf3 --- /dev/null +++ b/elixir/binary-search/README.md @@ -0,0 +1,71 @@ +# Binary Search + +Welcome to Binary Search on Exercism's Elixir Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Introduction + +You have stumbled upon a group of mathematicians who are also singer-songwriters. +They have written a song for each of their favorite numbers, and, as you can imagine, they have a lot of favorite numbers (like [0][zero] or [73][seventy-three] or [6174][kaprekars-constant]). + +You are curious to hear the song for your favorite number, but with so many songs to wade through, finding the right song could take a while. +Fortunately, they have organized their songs in a playlist sorted by the title — which is simply the number that the song is about. + +You realize that you can use a binary search algorithm to quickly find a song given the title. + +[zero]: https://en.wikipedia.org/wiki/0 +[seventy-three]: https://en.wikipedia.org/wiki/73_(number) +[kaprekars-constant]: https://en.wikipedia.org/wiki/6174_(number) + +## Instructions + +Your task is to implement a binary search algorithm. + +A binary search algorithm finds an item in a list by repeatedly splitting it in half, only keeping the half which contains the item we're looking for. +It allows us to quickly narrow down the possible locations of our item until we find it, or until we've eliminated all possible locations. + +~~~~exercism/caution +Binary search only works when a list has been sorted. +~~~~ + +The algorithm looks like this: + +- Find the middle element of a _sorted_ list and compare it with the item we're looking for. +- If the middle element is our item, then we're done! +- If the middle element is greater than our item, we can eliminate that element and all the elements **after** it. +- If the middle element is less than our item, we can eliminate that element and all the elements **before** it. +- If every element of the list has been eliminated then the item is not in the list. +- Otherwise, repeat the process on the part of the list that has not been eliminated. + +Here's an example: + +Let's say we're looking for the number 23 in the following sorted list: `[4, 8, 12, 16, 23, 28, 32]`. + +- We start by comparing 23 with the middle element, 16. +- Since 23 is greater than 16, we can eliminate the left half of the list, leaving us with `[23, 28, 32]`. +- We then compare 23 with the new middle element, 28. +- Since 23 is less than 28, we can eliminate the right half of the list: `[23]`. +- We've found our item. + +## Source + +### Created by + +- @bernardoamc + +### Contributed to by + +- @angelikatyborska +- @Cohen-Carlisle +- @devonestes +- @lpil +- @martinsvalin +- @neenjaw +- @parkerl +- @screamingjungle +- @sotojuan +- @waiting-for-dev + +### Based on + +Wikipedia - https://en.wikipedia.org/wiki/Binary_search_algorithm \ No newline at end of file diff --git a/elixir/binary-search/lib/binary_search.ex b/elixir/binary-search/lib/binary_search.ex new file mode 100644 index 0000000..9fb256a --- /dev/null +++ b/elixir/binary-search/lib/binary_search.ex @@ -0,0 +1,34 @@ +defmodule BinarySearch do + @doc """ + Searches for a key in the tuple using the binary search algorithm. + It returns :not_found if the key is not in the tuple. + Otherwise returns {:ok, index}. + + ## Examples + + iex> BinarySearch.search({}, 2) + :not_found + + iex> BinarySearch.search({1, 3, 5}, 2) + :not_found + + iex> BinarySearch.search({1, 3, 5}, 5) + {:ok, 2} + + """ + + @spec search(tuple, integer) :: {:ok, integer} | :not_found + def search({}, _value), do: :not_found + def search(numbers, value), do: do_search(numbers, value, 0, tuple_size(numbers) - 1) + + defp do_search(_numbers, _value, from, to) when from > to, do: :not_found + defp do_search(numbers, value, from, to) do + middle = div(from + to, 2) + element = elem(numbers, middle) + cond do + value == element -> {:ok, middle} + value < element -> do_search(numbers, value, from, middle - 1) + value > element -> do_search(numbers, value, middle + 1, to) + end + end +end diff --git a/elixir/binary-search/mix.exs b/elixir/binary-search/mix.exs new file mode 100644 index 0000000..36d00d4 --- /dev/null +++ b/elixir/binary-search/mix.exs @@ -0,0 +1,28 @@ +defmodule BinarySearch.MixProject do + use Mix.Project + + def project do + [ + app: :binary_search, + version: "0.1.0", + # elixir: "~> 1.8", + start_permanent: Mix.env() == :prod, + deps: deps() + ] + end + + # Run "mix help compile.app" to learn about applications. + def application do + [ + extra_applications: [:logger] + ] + end + + # Run "mix help deps" to learn about dependencies. + defp deps do + [ + # {:dep_from_hexpm, "~> 0.3.0"}, + # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"} + ] + end +end diff --git a/elixir/binary-search/test/binary_search_test.exs b/elixir/binary-search/test/binary_search_test.exs new file mode 100644 index 0000000..2c47f94 --- /dev/null +++ b/elixir/binary-search/test/binary_search_test.exs @@ -0,0 +1,57 @@ +defmodule BinarySearchTest do + use ExUnit.Case + + test "finds a value in a tuple with one element" do + assert BinarySearch.search({6}, 6) == {:ok, 0} + assert BinarySearch.search({3}, 3) == {:ok, 0} + end + + test "finds a value in the middle of a tuple" do + assert BinarySearch.search({1, 2, 4, 5, 6}, 4) == {:ok, 2} + assert BinarySearch.search({1, 3, 4, 6, 8, 9, 11}, 6) == {:ok, 3} + end + + test "finds a value at the beginning of a tuple" do + assert BinarySearch.search({1, 2, 4, 5, 6}, 1) == {:ok, 0} + assert BinarySearch.search({1, 3, 4, 5, 8, 9, 11}, 1) == {:ok, 0} + end + + test "finds a value at the end of a tuple" do + assert BinarySearch.search({1, 2, 4, 5, 6}, 6) == {:ok, 4} + assert BinarySearch.search({1, 3, 4, 5, 8, 9, 11}, 11) == {:ok, 6} + end + + test "finds a value in a tuple of odd length" do + tuple = {1, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 634} + assert BinarySearch.search(tuple, 144) == {:ok, 9} + end + + test "finds a value in a tuple of even length" do + tuple = {1, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377} + assert BinarySearch.search(tuple, 21) == {:ok, 5} + assert BinarySearch.search(tuple, 34) == {:ok, 6} + end + + test "identifies that a value is not included in the tuple" do + assert BinarySearch.search({2, 4, 6}, 3) == :not_found + assert BinarySearch.search({1, 3, 4, 6, 8, 9, 11}, 7) == :not_found + end + + test "a value smaller than the tuple's smallest value is not found" do + assert BinarySearch.search({2, 4, 6}, 1) == :not_found + assert BinarySearch.search({1, 3, 4, 6, 8, 9, 11}, 0) == :not_found + end + + test "a value larger than the tuple's largest value is not found" do + assert BinarySearch.search({2, 4, 6}, 9) == :not_found + assert BinarySearch.search({1, 3, 4, 6, 8, 9, 11}, 13) == :not_found + end + + test "nothing is found in an empty tuple" do + assert BinarySearch.search({}, 1) == :not_found + end + + test "nothing is found when the left and right bounds cross" do + assert BinarySearch.search({1, 2}, 0) == :not_found + end +end diff --git a/elixir/binary-search/test/test_helper.exs b/elixir/binary-search/test/test_helper.exs new file mode 100644 index 0000000..35fc5bf --- /dev/null +++ b/elixir/binary-search/test/test_helper.exs @@ -0,0 +1,2 @@ +ExUnit.start() +ExUnit.configure(exclude: :pending, trace: true)