Minimum Absolute Distance

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
1
down vote

favorite












I solved this problem




Given three sorted arrays A, B and C of not necessarily same sizes.



Calculate the minimum absolute difference between the maximum and
minimum number from the triplet a, b, c such that a, b, c belongs
arrays A, B, C respectively. i.e. minimize | max(a,b,c) - min(a,b,c)
|.



Example :



Input:



A : [ 1, 4, 5, 8, 10 ] B : [ 6, 9, 15 ] C : [ 2, 3, 6, 6 ]



Output:



1



Explanation:



We get the minimum difference for a=5, b=6, c=6 as |
max(a,b,c) - min(a,b,c) | = |6-5| = 1.




The idea behind my solution was if at each iteration we have the closest triplets we will be able to get the optimal absolute minimum.



I couldn't find a clean way to implement this logic. And my solution is this.



int Solution::solve(vector<int> &A, vector<int> &B, vector<int> &C) 
int answer = INT_MAX;
int a = 0, b = 0, c = 0;
while(a < A.size()


Could you please suggest how to avoid excessive if-else statements in the solution. Also, this solution wouldn't be scaleable for more array's so a more general approach would be helpful as well







share|improve this question



















  • You'll receive better reviews if you show a complete example. For example, I recommend that you edit to show the necessary #include lines, any using declarations, and a main() that shows how to call your function. It can really help reviewers if they are able to compile and run your program.
    – Toby Speight
    Apr 9 at 10:11










  • BTW, this is an interesting question, and I would like to answer it, but I'll give you time to add the extra code before I add my answer.
    – Toby Speight
    Apr 9 at 10:38










  • This is a online judge which only requires us to write the particular method. The pluming the goes with this solution isn't really visible to us. The input is given to us and we are only required to return the answer. You can check out the link if you want to run your solution against the test cases.
    – thebenman
    Apr 9 at 15:44
















up vote
1
down vote

favorite












I solved this problem




Given three sorted arrays A, B and C of not necessarily same sizes.



Calculate the minimum absolute difference between the maximum and
minimum number from the triplet a, b, c such that a, b, c belongs
arrays A, B, C respectively. i.e. minimize | max(a,b,c) - min(a,b,c)
|.



Example :



Input:



A : [ 1, 4, 5, 8, 10 ] B : [ 6, 9, 15 ] C : [ 2, 3, 6, 6 ]



Output:



1



Explanation:



We get the minimum difference for a=5, b=6, c=6 as |
max(a,b,c) - min(a,b,c) | = |6-5| = 1.




The idea behind my solution was if at each iteration we have the closest triplets we will be able to get the optimal absolute minimum.



I couldn't find a clean way to implement this logic. And my solution is this.



int Solution::solve(vector<int> &A, vector<int> &B, vector<int> &C) 
int answer = INT_MAX;
int a = 0, b = 0, c = 0;
while(a < A.size()


Could you please suggest how to avoid excessive if-else statements in the solution. Also, this solution wouldn't be scaleable for more array's so a more general approach would be helpful as well







share|improve this question



















  • You'll receive better reviews if you show a complete example. For example, I recommend that you edit to show the necessary #include lines, any using declarations, and a main() that shows how to call your function. It can really help reviewers if they are able to compile and run your program.
    – Toby Speight
    Apr 9 at 10:11










  • BTW, this is an interesting question, and I would like to answer it, but I'll give you time to add the extra code before I add my answer.
    – Toby Speight
    Apr 9 at 10:38










  • This is a online judge which only requires us to write the particular method. The pluming the goes with this solution isn't really visible to us. The input is given to us and we are only required to return the answer. You can check out the link if you want to run your solution against the test cases.
    – thebenman
    Apr 9 at 15:44












up vote
1
down vote

favorite









up vote
1
down vote

favorite











I solved this problem




Given three sorted arrays A, B and C of not necessarily same sizes.



Calculate the minimum absolute difference between the maximum and
minimum number from the triplet a, b, c such that a, b, c belongs
arrays A, B, C respectively. i.e. minimize | max(a,b,c) - min(a,b,c)
|.



Example :



Input:



A : [ 1, 4, 5, 8, 10 ] B : [ 6, 9, 15 ] C : [ 2, 3, 6, 6 ]



Output:



1



Explanation:



We get the minimum difference for a=5, b=6, c=6 as |
max(a,b,c) - min(a,b,c) | = |6-5| = 1.




The idea behind my solution was if at each iteration we have the closest triplets we will be able to get the optimal absolute minimum.



I couldn't find a clean way to implement this logic. And my solution is this.



int Solution::solve(vector<int> &A, vector<int> &B, vector<int> &C) 
int answer = INT_MAX;
int a = 0, b = 0, c = 0;
while(a < A.size()


Could you please suggest how to avoid excessive if-else statements in the solution. Also, this solution wouldn't be scaleable for more array's so a more general approach would be helpful as well







share|improve this question











I solved this problem




Given three sorted arrays A, B and C of not necessarily same sizes.



Calculate the minimum absolute difference between the maximum and
minimum number from the triplet a, b, c such that a, b, c belongs
arrays A, B, C respectively. i.e. minimize | max(a,b,c) - min(a,b,c)
|.



Example :



Input:



A : [ 1, 4, 5, 8, 10 ] B : [ 6, 9, 15 ] C : [ 2, 3, 6, 6 ]



Output:



1



Explanation:



We get the minimum difference for a=5, b=6, c=6 as |
max(a,b,c) - min(a,b,c) | = |6-5| = 1.




The idea behind my solution was if at each iteration we have the closest triplets we will be able to get the optimal absolute minimum.



I couldn't find a clean way to implement this logic. And my solution is this.



int Solution::solve(vector<int> &A, vector<int> &B, vector<int> &C) 
int answer = INT_MAX;
int a = 0, b = 0, c = 0;
while(a < A.size()


Could you please suggest how to avoid excessive if-else statements in the solution. Also, this solution wouldn't be scaleable for more array's so a more general approach would be helpful as well









share|improve this question










share|improve this question




share|improve this question









asked Apr 7 at 7:21









thebenman

449314




449314











  • You'll receive better reviews if you show a complete example. For example, I recommend that you edit to show the necessary #include lines, any using declarations, and a main() that shows how to call your function. It can really help reviewers if they are able to compile and run your program.
    – Toby Speight
    Apr 9 at 10:11










  • BTW, this is an interesting question, and I would like to answer it, but I'll give you time to add the extra code before I add my answer.
    – Toby Speight
    Apr 9 at 10:38










  • This is a online judge which only requires us to write the particular method. The pluming the goes with this solution isn't really visible to us. The input is given to us and we are only required to return the answer. You can check out the link if you want to run your solution against the test cases.
    – thebenman
    Apr 9 at 15:44
















  • You'll receive better reviews if you show a complete example. For example, I recommend that you edit to show the necessary #include lines, any using declarations, and a main() that shows how to call your function. It can really help reviewers if they are able to compile and run your program.
    – Toby Speight
    Apr 9 at 10:11










  • BTW, this is an interesting question, and I would like to answer it, but I'll give you time to add the extra code before I add my answer.
    – Toby Speight
    Apr 9 at 10:38










  • This is a online judge which only requires us to write the particular method. The pluming the goes with this solution isn't really visible to us. The input is given to us and we are only required to return the answer. You can check out the link if you want to run your solution against the test cases.
    – thebenman
    Apr 9 at 15:44















You'll receive better reviews if you show a complete example. For example, I recommend that you edit to show the necessary #include lines, any using declarations, and a main() that shows how to call your function. It can really help reviewers if they are able to compile and run your program.
– Toby Speight
Apr 9 at 10:11




You'll receive better reviews if you show a complete example. For example, I recommend that you edit to show the necessary #include lines, any using declarations, and a main() that shows how to call your function. It can really help reviewers if they are able to compile and run your program.
– Toby Speight
Apr 9 at 10:11












BTW, this is an interesting question, and I would like to answer it, but I'll give you time to add the extra code before I add my answer.
– Toby Speight
Apr 9 at 10:38




BTW, this is an interesting question, and I would like to answer it, but I'll give you time to add the extra code before I add my answer.
– Toby Speight
Apr 9 at 10:38












This is a online judge which only requires us to write the particular method. The pluming the goes with this solution isn't really visible to us. The input is given to us and we are only required to return the answer. You can check out the link if you want to run your solution against the test cases.
– thebenman
Apr 9 at 15:44




This is a online judge which only requires us to write the particular method. The pluming the goes with this solution isn't really visible to us. The input is given to us and we are only required to return the answer. You can check out the link if you want to run your solution against the test cases.
– thebenman
Apr 9 at 15:44










1 Answer
1






active

oldest

votes

















up vote
2
down vote



accepted










Headers and namespace



We're missing headers



#include <algorithm>
#include <climits>
#include <cmath>
#include <vector>


Also, it appears the code assumes using namespace std. Bringing all names in from a namespace is problematic; namespace std particularly so. See Why is “using namespace std” considered bad practice?.



Code the solution, and adapt it to fit



The use of the Solution class appears to be an artefact of your test environment. Since the function doesn't need any state, prefer to write it as you normally would:



// we'll change this signature later
static int minimum_range(const std::vector<int> &A,
const std::vector<int> &B,
const std::vector<int> &C);


And then provide Solution as a thin wrapper around it, like:



int Solution::solve(std::vector<int> &A, std::vector<int> &B, std::vector<int> &C) 
return minimum_range(a, b, c);



We can then call minimum_range from our test program much more simply:



int main()

return
+ (0 != minimum_range(std::vector5))
// add more tests here
+ (1 != minimum_range(std::vector1, 4, 5, 8, 10, std::vector6, 9, 15, std::vector2, 3, 6, 6));



We might make an exception to this general advice in the case of competitive performance-oriented programming; but then code review goes out of the window entirely, as speed trumps style and maintainability there.



Algorithm



The method is basically sound; it scales well to large vectors, but doesn't scale well to many vectors (as you note in the description).



For general code, prefer to think in iterators rather than indexes. The code will be equivalent, but usable with more types than just vectors and arrays.



If we keep a collection of iterators (one for each of the arrays), then at each step, we want to advance the one that points to the smallest element (added complication - we need to tie-break by choosing the one that advances the least). We have finished when that one reaches the end of its array; that means we also need to remember the end iterator for each array. That means we end up storing a range for each input vector.



template<typename Iter>
struct range

Iter current;
Iter const end;
;

template<typename T>
range<typename std::vector<T>::const_iterator> make_range(const std::vector<T> &v)

return v.begin(), v.end();



Now we can transform the vectors into ranges:



static int minimum_range(const std::vector<int> &A,
const std::vector<int> &B,
const std::vector<int> &C)
{
auto range = make_range(A), make_range(B), make_range(C) ;

auto minmax_range = std::minmax_element(range.begin(), range.end());


For the std::minmax_element() call to work, we'll need a suitable < operator in our range. Add this:



bool operator<(const range& other) const

if (current == end) return false;
if (other.current == other.end) return true;
auto a = *current, b = *other.current;
if (a != b) return a < b;

// it's a draw - which one advances least?
if (current+1 == end) return false;
if (other.current+1 == other.end) return true;
a = current[1], b = other.current[1];
return a < b;



Now let's go back to the algorithm and make it iterate:



auto minmax_range = std::minmax_element(r.begin(), r.end());
auto& min_range = minmax_range.first;
auto& max_range = minmax_range.second;
int best = **max_range - **min_range;

while ((++*min_range).current != min_range->end)
minmax_range = std::minmax_element(r.begin(), r.end());
best = std::min(best, **max_range - **min_range);
if (best == 0)
break; // we can't improve on zero




There's some stuff to explain there. We create aliases for the minmax_range, which is a pair of iterators to range objects. We use the dereference operator, which needs defining in range to give the current first element of the range:



auto operator*() const return *current; 


The loop control starts by incrementing the lowest range - that needs an implementation:



range& operator++() ++current; return *this; 


If that produced a range that has reached the end, then we've considered all the possibilities.



Inside the loop, we find the new min and max (remember that min_range and max_range still alias the members of minmax_range), and update best if required.



Extension to many inputs



We can now use a std::initializer_list to supply an arbitrary number of collections of any kind of (comparable) element. I'll use C++17 for this (so we can write an implicit constructor for range, using a deduction guide).



#include <algorithm>
#include <vector>

template<typename Iter>
struct range

Iter current;
Iter const end;

template<typename T>
range(const T& v)
: currentv.begin(),
endv.end()



explicit operator bool() const return current != end;

auto operator*() const return *current;

range& operator++() ++current; return *this;

bool operator<(const range& other) const

return *this && (!other
;

// deduction guide
template<typename Container>
range(Container c) -> range<typename Container::const_iterator>;


template<typename Iter>
static auto minimum_range(std::initializer_list<range<Iter>> containers)

using std::begin;
using std::end;

// Copy initializer list to read/write container
std::vector r(begin(containers), end(containers));

auto minmax_range = std::minmax_element(begin(r), end(r));
auto& min_range = minmax_range.first;
auto& max_range = minmax_range.second;
auto best = **max_range - **min_range;

while (++*min_range)
minmax_range = std::minmax_element(begin(r), end(r));
best = std::min(best, **max_range - **min_range);


return best;


// convert containers to ranges and forward
template<typename... Args>
static auto minimum_range(const Args&... args)

return minimum_range(range(args)...);





#include <list>
int main()

return
+ (0 != minimum_range(std::list5))
+ (0.5 != minimum_range(std::vector0.0, std::vector0.0, std::vector0.5))
+ (6 != minimum_range(std::vector6,30, std::vector10,12, std::vector6,15,30, std::vector12))
+ (1 != minimum_range(std::vector0, 6, 10, std::vector5, std::vector5))
// add more tests here
+ (1 != minimum_range(std::list1, 4, 5, 8, 10, std::list6, 9, 15, std::list2, 3, 6, 6));



I was unable to construct a test that proves the need for the tie-break in operator<, so I removed that code. I shouldn't have written it in the first place without a test to exercise it, of course...






share|improve this answer





















    Your Answer




    StackExchange.ifUsing("editor", function ()
    return StackExchange.using("mathjaxEditing", function ()
    StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
    StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
    );
    );
    , "mathjax-editing");

    StackExchange.ifUsing("editor", function ()
    StackExchange.using("externalEditor", function ()
    StackExchange.using("snippets", function ()
    StackExchange.snippets.init();
    );
    );
    , "code-snippets");

    StackExchange.ready(function()
    var channelOptions =
    tags: "".split(" "),
    id: "196"
    ;
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function()
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled)
    StackExchange.using("snippets", function()
    createEditor();
    );

    else
    createEditor();

    );

    function createEditor()
    StackExchange.prepareEditor(
    heartbeatType: 'answer',
    convertImagesToLinks: false,
    noModals: false,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    );



    );








     

    draft saved


    draft discarded


















    StackExchange.ready(
    function ()
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f191461%2fminimum-absolute-distance%23new-answer', 'question_page');

    );

    Post as a guest






























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    2
    down vote



    accepted










    Headers and namespace



    We're missing headers



    #include <algorithm>
    #include <climits>
    #include <cmath>
    #include <vector>


    Also, it appears the code assumes using namespace std. Bringing all names in from a namespace is problematic; namespace std particularly so. See Why is “using namespace std” considered bad practice?.



    Code the solution, and adapt it to fit



    The use of the Solution class appears to be an artefact of your test environment. Since the function doesn't need any state, prefer to write it as you normally would:



    // we'll change this signature later
    static int minimum_range(const std::vector<int> &A,
    const std::vector<int> &B,
    const std::vector<int> &C);


    And then provide Solution as a thin wrapper around it, like:



    int Solution::solve(std::vector<int> &A, std::vector<int> &B, std::vector<int> &C) 
    return minimum_range(a, b, c);



    We can then call minimum_range from our test program much more simply:



    int main()

    return
    + (0 != minimum_range(std::vector5))
    // add more tests here
    + (1 != minimum_range(std::vector1, 4, 5, 8, 10, std::vector6, 9, 15, std::vector2, 3, 6, 6));



    We might make an exception to this general advice in the case of competitive performance-oriented programming; but then code review goes out of the window entirely, as speed trumps style and maintainability there.



    Algorithm



    The method is basically sound; it scales well to large vectors, but doesn't scale well to many vectors (as you note in the description).



    For general code, prefer to think in iterators rather than indexes. The code will be equivalent, but usable with more types than just vectors and arrays.



    If we keep a collection of iterators (one for each of the arrays), then at each step, we want to advance the one that points to the smallest element (added complication - we need to tie-break by choosing the one that advances the least). We have finished when that one reaches the end of its array; that means we also need to remember the end iterator for each array. That means we end up storing a range for each input vector.



    template<typename Iter>
    struct range

    Iter current;
    Iter const end;
    ;

    template<typename T>
    range<typename std::vector<T>::const_iterator> make_range(const std::vector<T> &v)

    return v.begin(), v.end();



    Now we can transform the vectors into ranges:



    static int minimum_range(const std::vector<int> &A,
    const std::vector<int> &B,
    const std::vector<int> &C)
    {
    auto range = make_range(A), make_range(B), make_range(C) ;

    auto minmax_range = std::minmax_element(range.begin(), range.end());


    For the std::minmax_element() call to work, we'll need a suitable < operator in our range. Add this:



    bool operator<(const range& other) const

    if (current == end) return false;
    if (other.current == other.end) return true;
    auto a = *current, b = *other.current;
    if (a != b) return a < b;

    // it's a draw - which one advances least?
    if (current+1 == end) return false;
    if (other.current+1 == other.end) return true;
    a = current[1], b = other.current[1];
    return a < b;



    Now let's go back to the algorithm and make it iterate:



    auto minmax_range = std::minmax_element(r.begin(), r.end());
    auto& min_range = minmax_range.first;
    auto& max_range = minmax_range.second;
    int best = **max_range - **min_range;

    while ((++*min_range).current != min_range->end)
    minmax_range = std::minmax_element(r.begin(), r.end());
    best = std::min(best, **max_range - **min_range);
    if (best == 0)
    break; // we can't improve on zero




    There's some stuff to explain there. We create aliases for the minmax_range, which is a pair of iterators to range objects. We use the dereference operator, which needs defining in range to give the current first element of the range:



    auto operator*() const return *current; 


    The loop control starts by incrementing the lowest range - that needs an implementation:



    range& operator++() ++current; return *this; 


    If that produced a range that has reached the end, then we've considered all the possibilities.



    Inside the loop, we find the new min and max (remember that min_range and max_range still alias the members of minmax_range), and update best if required.



    Extension to many inputs



    We can now use a std::initializer_list to supply an arbitrary number of collections of any kind of (comparable) element. I'll use C++17 for this (so we can write an implicit constructor for range, using a deduction guide).



    #include <algorithm>
    #include <vector>

    template<typename Iter>
    struct range

    Iter current;
    Iter const end;

    template<typename T>
    range(const T& v)
    : currentv.begin(),
    endv.end()



    explicit operator bool() const return current != end;

    auto operator*() const return *current;

    range& operator++() ++current; return *this;

    bool operator<(const range& other) const

    return *this && (!other
    ;

    // deduction guide
    template<typename Container>
    range(Container c) -> range<typename Container::const_iterator>;


    template<typename Iter>
    static auto minimum_range(std::initializer_list<range<Iter>> containers)

    using std::begin;
    using std::end;

    // Copy initializer list to read/write container
    std::vector r(begin(containers), end(containers));

    auto minmax_range = std::minmax_element(begin(r), end(r));
    auto& min_range = minmax_range.first;
    auto& max_range = minmax_range.second;
    auto best = **max_range - **min_range;

    while (++*min_range)
    minmax_range = std::minmax_element(begin(r), end(r));
    best = std::min(best, **max_range - **min_range);


    return best;


    // convert containers to ranges and forward
    template<typename... Args>
    static auto minimum_range(const Args&... args)

    return minimum_range(range(args)...);





    #include <list>
    int main()

    return
    + (0 != minimum_range(std::list5))
    + (0.5 != minimum_range(std::vector0.0, std::vector0.0, std::vector0.5))
    + (6 != minimum_range(std::vector6,30, std::vector10,12, std::vector6,15,30, std::vector12))
    + (1 != minimum_range(std::vector0, 6, 10, std::vector5, std::vector5))
    // add more tests here
    + (1 != minimum_range(std::list1, 4, 5, 8, 10, std::list6, 9, 15, std::list2, 3, 6, 6));



    I was unable to construct a test that proves the need for the tie-break in operator<, so I removed that code. I shouldn't have written it in the first place without a test to exercise it, of course...






    share|improve this answer

























      up vote
      2
      down vote



      accepted










      Headers and namespace



      We're missing headers



      #include <algorithm>
      #include <climits>
      #include <cmath>
      #include <vector>


      Also, it appears the code assumes using namespace std. Bringing all names in from a namespace is problematic; namespace std particularly so. See Why is “using namespace std” considered bad practice?.



      Code the solution, and adapt it to fit



      The use of the Solution class appears to be an artefact of your test environment. Since the function doesn't need any state, prefer to write it as you normally would:



      // we'll change this signature later
      static int minimum_range(const std::vector<int> &A,
      const std::vector<int> &B,
      const std::vector<int> &C);


      And then provide Solution as a thin wrapper around it, like:



      int Solution::solve(std::vector<int> &A, std::vector<int> &B, std::vector<int> &C) 
      return minimum_range(a, b, c);



      We can then call minimum_range from our test program much more simply:



      int main()

      return
      + (0 != minimum_range(std::vector5))
      // add more tests here
      + (1 != minimum_range(std::vector1, 4, 5, 8, 10, std::vector6, 9, 15, std::vector2, 3, 6, 6));



      We might make an exception to this general advice in the case of competitive performance-oriented programming; but then code review goes out of the window entirely, as speed trumps style and maintainability there.



      Algorithm



      The method is basically sound; it scales well to large vectors, but doesn't scale well to many vectors (as you note in the description).



      For general code, prefer to think in iterators rather than indexes. The code will be equivalent, but usable with more types than just vectors and arrays.



      If we keep a collection of iterators (one for each of the arrays), then at each step, we want to advance the one that points to the smallest element (added complication - we need to tie-break by choosing the one that advances the least). We have finished when that one reaches the end of its array; that means we also need to remember the end iterator for each array. That means we end up storing a range for each input vector.



      template<typename Iter>
      struct range

      Iter current;
      Iter const end;
      ;

      template<typename T>
      range<typename std::vector<T>::const_iterator> make_range(const std::vector<T> &v)

      return v.begin(), v.end();



      Now we can transform the vectors into ranges:



      static int minimum_range(const std::vector<int> &A,
      const std::vector<int> &B,
      const std::vector<int> &C)
      {
      auto range = make_range(A), make_range(B), make_range(C) ;

      auto minmax_range = std::minmax_element(range.begin(), range.end());


      For the std::minmax_element() call to work, we'll need a suitable < operator in our range. Add this:



      bool operator<(const range& other) const

      if (current == end) return false;
      if (other.current == other.end) return true;
      auto a = *current, b = *other.current;
      if (a != b) return a < b;

      // it's a draw - which one advances least?
      if (current+1 == end) return false;
      if (other.current+1 == other.end) return true;
      a = current[1], b = other.current[1];
      return a < b;



      Now let's go back to the algorithm and make it iterate:



      auto minmax_range = std::minmax_element(r.begin(), r.end());
      auto& min_range = minmax_range.first;
      auto& max_range = minmax_range.second;
      int best = **max_range - **min_range;

      while ((++*min_range).current != min_range->end)
      minmax_range = std::minmax_element(r.begin(), r.end());
      best = std::min(best, **max_range - **min_range);
      if (best == 0)
      break; // we can't improve on zero




      There's some stuff to explain there. We create aliases for the minmax_range, which is a pair of iterators to range objects. We use the dereference operator, which needs defining in range to give the current first element of the range:



      auto operator*() const return *current; 


      The loop control starts by incrementing the lowest range - that needs an implementation:



      range& operator++() ++current; return *this; 


      If that produced a range that has reached the end, then we've considered all the possibilities.



      Inside the loop, we find the new min and max (remember that min_range and max_range still alias the members of minmax_range), and update best if required.



      Extension to many inputs



      We can now use a std::initializer_list to supply an arbitrary number of collections of any kind of (comparable) element. I'll use C++17 for this (so we can write an implicit constructor for range, using a deduction guide).



      #include <algorithm>
      #include <vector>

      template<typename Iter>
      struct range

      Iter current;
      Iter const end;

      template<typename T>
      range(const T& v)
      : currentv.begin(),
      endv.end()



      explicit operator bool() const return current != end;

      auto operator*() const return *current;

      range& operator++() ++current; return *this;

      bool operator<(const range& other) const

      return *this && (!other
      ;

      // deduction guide
      template<typename Container>
      range(Container c) -> range<typename Container::const_iterator>;


      template<typename Iter>
      static auto minimum_range(std::initializer_list<range<Iter>> containers)

      using std::begin;
      using std::end;

      // Copy initializer list to read/write container
      std::vector r(begin(containers), end(containers));

      auto minmax_range = std::minmax_element(begin(r), end(r));
      auto& min_range = minmax_range.first;
      auto& max_range = minmax_range.second;
      auto best = **max_range - **min_range;

      while (++*min_range)
      minmax_range = std::minmax_element(begin(r), end(r));
      best = std::min(best, **max_range - **min_range);


      return best;


      // convert containers to ranges and forward
      template<typename... Args>
      static auto minimum_range(const Args&... args)

      return minimum_range(range(args)...);





      #include <list>
      int main()

      return
      + (0 != minimum_range(std::list5))
      + (0.5 != minimum_range(std::vector0.0, std::vector0.0, std::vector0.5))
      + (6 != minimum_range(std::vector6,30, std::vector10,12, std::vector6,15,30, std::vector12))
      + (1 != minimum_range(std::vector0, 6, 10, std::vector5, std::vector5))
      // add more tests here
      + (1 != minimum_range(std::list1, 4, 5, 8, 10, std::list6, 9, 15, std::list2, 3, 6, 6));



      I was unable to construct a test that proves the need for the tie-break in operator<, so I removed that code. I shouldn't have written it in the first place without a test to exercise it, of course...






      share|improve this answer























        up vote
        2
        down vote



        accepted







        up vote
        2
        down vote



        accepted






        Headers and namespace



        We're missing headers



        #include <algorithm>
        #include <climits>
        #include <cmath>
        #include <vector>


        Also, it appears the code assumes using namespace std. Bringing all names in from a namespace is problematic; namespace std particularly so. See Why is “using namespace std” considered bad practice?.



        Code the solution, and adapt it to fit



        The use of the Solution class appears to be an artefact of your test environment. Since the function doesn't need any state, prefer to write it as you normally would:



        // we'll change this signature later
        static int minimum_range(const std::vector<int> &A,
        const std::vector<int> &B,
        const std::vector<int> &C);


        And then provide Solution as a thin wrapper around it, like:



        int Solution::solve(std::vector<int> &A, std::vector<int> &B, std::vector<int> &C) 
        return minimum_range(a, b, c);



        We can then call minimum_range from our test program much more simply:



        int main()

        return
        + (0 != minimum_range(std::vector5))
        // add more tests here
        + (1 != minimum_range(std::vector1, 4, 5, 8, 10, std::vector6, 9, 15, std::vector2, 3, 6, 6));



        We might make an exception to this general advice in the case of competitive performance-oriented programming; but then code review goes out of the window entirely, as speed trumps style and maintainability there.



        Algorithm



        The method is basically sound; it scales well to large vectors, but doesn't scale well to many vectors (as you note in the description).



        For general code, prefer to think in iterators rather than indexes. The code will be equivalent, but usable with more types than just vectors and arrays.



        If we keep a collection of iterators (one for each of the arrays), then at each step, we want to advance the one that points to the smallest element (added complication - we need to tie-break by choosing the one that advances the least). We have finished when that one reaches the end of its array; that means we also need to remember the end iterator for each array. That means we end up storing a range for each input vector.



        template<typename Iter>
        struct range

        Iter current;
        Iter const end;
        ;

        template<typename T>
        range<typename std::vector<T>::const_iterator> make_range(const std::vector<T> &v)

        return v.begin(), v.end();



        Now we can transform the vectors into ranges:



        static int minimum_range(const std::vector<int> &A,
        const std::vector<int> &B,
        const std::vector<int> &C)
        {
        auto range = make_range(A), make_range(B), make_range(C) ;

        auto minmax_range = std::minmax_element(range.begin(), range.end());


        For the std::minmax_element() call to work, we'll need a suitable < operator in our range. Add this:



        bool operator<(const range& other) const

        if (current == end) return false;
        if (other.current == other.end) return true;
        auto a = *current, b = *other.current;
        if (a != b) return a < b;

        // it's a draw - which one advances least?
        if (current+1 == end) return false;
        if (other.current+1 == other.end) return true;
        a = current[1], b = other.current[1];
        return a < b;



        Now let's go back to the algorithm and make it iterate:



        auto minmax_range = std::minmax_element(r.begin(), r.end());
        auto& min_range = minmax_range.first;
        auto& max_range = minmax_range.second;
        int best = **max_range - **min_range;

        while ((++*min_range).current != min_range->end)
        minmax_range = std::minmax_element(r.begin(), r.end());
        best = std::min(best, **max_range - **min_range);
        if (best == 0)
        break; // we can't improve on zero




        There's some stuff to explain there. We create aliases for the minmax_range, which is a pair of iterators to range objects. We use the dereference operator, which needs defining in range to give the current first element of the range:



        auto operator*() const return *current; 


        The loop control starts by incrementing the lowest range - that needs an implementation:



        range& operator++() ++current; return *this; 


        If that produced a range that has reached the end, then we've considered all the possibilities.



        Inside the loop, we find the new min and max (remember that min_range and max_range still alias the members of minmax_range), and update best if required.



        Extension to many inputs



        We can now use a std::initializer_list to supply an arbitrary number of collections of any kind of (comparable) element. I'll use C++17 for this (so we can write an implicit constructor for range, using a deduction guide).



        #include <algorithm>
        #include <vector>

        template<typename Iter>
        struct range

        Iter current;
        Iter const end;

        template<typename T>
        range(const T& v)
        : currentv.begin(),
        endv.end()



        explicit operator bool() const return current != end;

        auto operator*() const return *current;

        range& operator++() ++current; return *this;

        bool operator<(const range& other) const

        return *this && (!other
        ;

        // deduction guide
        template<typename Container>
        range(Container c) -> range<typename Container::const_iterator>;


        template<typename Iter>
        static auto minimum_range(std::initializer_list<range<Iter>> containers)

        using std::begin;
        using std::end;

        // Copy initializer list to read/write container
        std::vector r(begin(containers), end(containers));

        auto minmax_range = std::minmax_element(begin(r), end(r));
        auto& min_range = minmax_range.first;
        auto& max_range = minmax_range.second;
        auto best = **max_range - **min_range;

        while (++*min_range)
        minmax_range = std::minmax_element(begin(r), end(r));
        best = std::min(best, **max_range - **min_range);


        return best;


        // convert containers to ranges and forward
        template<typename... Args>
        static auto minimum_range(const Args&... args)

        return minimum_range(range(args)...);





        #include <list>
        int main()

        return
        + (0 != minimum_range(std::list5))
        + (0.5 != minimum_range(std::vector0.0, std::vector0.0, std::vector0.5))
        + (6 != minimum_range(std::vector6,30, std::vector10,12, std::vector6,15,30, std::vector12))
        + (1 != minimum_range(std::vector0, 6, 10, std::vector5, std::vector5))
        // add more tests here
        + (1 != minimum_range(std::list1, 4, 5, 8, 10, std::list6, 9, 15, std::list2, 3, 6, 6));



        I was unable to construct a test that proves the need for the tie-break in operator<, so I removed that code. I shouldn't have written it in the first place without a test to exercise it, of course...






        share|improve this answer













        Headers and namespace



        We're missing headers



        #include <algorithm>
        #include <climits>
        #include <cmath>
        #include <vector>


        Also, it appears the code assumes using namespace std. Bringing all names in from a namespace is problematic; namespace std particularly so. See Why is “using namespace std” considered bad practice?.



        Code the solution, and adapt it to fit



        The use of the Solution class appears to be an artefact of your test environment. Since the function doesn't need any state, prefer to write it as you normally would:



        // we'll change this signature later
        static int minimum_range(const std::vector<int> &A,
        const std::vector<int> &B,
        const std::vector<int> &C);


        And then provide Solution as a thin wrapper around it, like:



        int Solution::solve(std::vector<int> &A, std::vector<int> &B, std::vector<int> &C) 
        return minimum_range(a, b, c);



        We can then call minimum_range from our test program much more simply:



        int main()

        return
        + (0 != minimum_range(std::vector5))
        // add more tests here
        + (1 != minimum_range(std::vector1, 4, 5, 8, 10, std::vector6, 9, 15, std::vector2, 3, 6, 6));



        We might make an exception to this general advice in the case of competitive performance-oriented programming; but then code review goes out of the window entirely, as speed trumps style and maintainability there.



        Algorithm



        The method is basically sound; it scales well to large vectors, but doesn't scale well to many vectors (as you note in the description).



        For general code, prefer to think in iterators rather than indexes. The code will be equivalent, but usable with more types than just vectors and arrays.



        If we keep a collection of iterators (one for each of the arrays), then at each step, we want to advance the one that points to the smallest element (added complication - we need to tie-break by choosing the one that advances the least). We have finished when that one reaches the end of its array; that means we also need to remember the end iterator for each array. That means we end up storing a range for each input vector.



        template<typename Iter>
        struct range

        Iter current;
        Iter const end;
        ;

        template<typename T>
        range<typename std::vector<T>::const_iterator> make_range(const std::vector<T> &v)

        return v.begin(), v.end();



        Now we can transform the vectors into ranges:



        static int minimum_range(const std::vector<int> &A,
        const std::vector<int> &B,
        const std::vector<int> &C)
        {
        auto range = make_range(A), make_range(B), make_range(C) ;

        auto minmax_range = std::minmax_element(range.begin(), range.end());


        For the std::minmax_element() call to work, we'll need a suitable < operator in our range. Add this:



        bool operator<(const range& other) const

        if (current == end) return false;
        if (other.current == other.end) return true;
        auto a = *current, b = *other.current;
        if (a != b) return a < b;

        // it's a draw - which one advances least?
        if (current+1 == end) return false;
        if (other.current+1 == other.end) return true;
        a = current[1], b = other.current[1];
        return a < b;



        Now let's go back to the algorithm and make it iterate:



        auto minmax_range = std::minmax_element(r.begin(), r.end());
        auto& min_range = minmax_range.first;
        auto& max_range = minmax_range.second;
        int best = **max_range - **min_range;

        while ((++*min_range).current != min_range->end)
        minmax_range = std::minmax_element(r.begin(), r.end());
        best = std::min(best, **max_range - **min_range);
        if (best == 0)
        break; // we can't improve on zero




        There's some stuff to explain there. We create aliases for the minmax_range, which is a pair of iterators to range objects. We use the dereference operator, which needs defining in range to give the current first element of the range:



        auto operator*() const return *current; 


        The loop control starts by incrementing the lowest range - that needs an implementation:



        range& operator++() ++current; return *this; 


        If that produced a range that has reached the end, then we've considered all the possibilities.



        Inside the loop, we find the new min and max (remember that min_range and max_range still alias the members of minmax_range), and update best if required.



        Extension to many inputs



        We can now use a std::initializer_list to supply an arbitrary number of collections of any kind of (comparable) element. I'll use C++17 for this (so we can write an implicit constructor for range, using a deduction guide).



        #include <algorithm>
        #include <vector>

        template<typename Iter>
        struct range

        Iter current;
        Iter const end;

        template<typename T>
        range(const T& v)
        : currentv.begin(),
        endv.end()



        explicit operator bool() const return current != end;

        auto operator*() const return *current;

        range& operator++() ++current; return *this;

        bool operator<(const range& other) const

        return *this && (!other
        ;

        // deduction guide
        template<typename Container>
        range(Container c) -> range<typename Container::const_iterator>;


        template<typename Iter>
        static auto minimum_range(std::initializer_list<range<Iter>> containers)

        using std::begin;
        using std::end;

        // Copy initializer list to read/write container
        std::vector r(begin(containers), end(containers));

        auto minmax_range = std::minmax_element(begin(r), end(r));
        auto& min_range = minmax_range.first;
        auto& max_range = minmax_range.second;
        auto best = **max_range - **min_range;

        while (++*min_range)
        minmax_range = std::minmax_element(begin(r), end(r));
        best = std::min(best, **max_range - **min_range);


        return best;


        // convert containers to ranges and forward
        template<typename... Args>
        static auto minimum_range(const Args&... args)

        return minimum_range(range(args)...);





        #include <list>
        int main()

        return
        + (0 != minimum_range(std::list5))
        + (0.5 != minimum_range(std::vector0.0, std::vector0.0, std::vector0.5))
        + (6 != minimum_range(std::vector6,30, std::vector10,12, std::vector6,15,30, std::vector12))
        + (1 != minimum_range(std::vector0, 6, 10, std::vector5, std::vector5))
        // add more tests here
        + (1 != minimum_range(std::list1, 4, 5, 8, 10, std::list6, 9, 15, std::list2, 3, 6, 6));



        I was unable to construct a test that proves the need for the tie-break in operator<, so I removed that code. I shouldn't have written it in the first place without a test to exercise it, of course...







        share|improve this answer













        share|improve this answer



        share|improve this answer











        answered Apr 9 at 18:44









        Toby Speight

        17.5k13489




        17.5k13489






















             

            draft saved


            draft discarded


























             


            draft saved


            draft discarded














            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f191461%2fminimum-absolute-distance%23new-answer', 'question_page');

            );

            Post as a guest













































































            Popular posts from this blog

            Python Lists

            Aion

            JavaScript Array Iteration Methods