在避免代码重复和冲突名称的同时,如何实现多个版本的同一算法

How can I implement multiple versions of the same algorithm while avoiding code duplication and conflicting names?

本文关键字:何实现 实现 算法 版本 代码 冲突      更新时间:2023-10-16

我已经在C 中开发了插入排序和QuickSort算法。现在,我打算至少创建QuickSort算法的四个变体。他们将在选择枢轴以及是否将插入排序用于小列表方面有所不同。在Java或C#中,为了避免代码重复和相互冲突的名称,我将在单独的类文件中实现QuickSort算法的每个版本,并使用继承。具体来说,我将创建以下类:

  • QuicksortFixedPivot
  • QuicksortRandomPivot
  • QuicksortFixedPivotInsertion-使用插入排序对k元素的子阵列进行分类
  • QuicksortRandomPivotInsertion

但是,从我的理解中,像QuickSort这样的"独立"算法通常不在C 的类中实现。

以下是我对QuickSort的初步实现。

QuickSort.hpp

#pragma once
#include <vector>
namespace algorithms 
{
   void quicksort(std::vector<int> &list);
   void quicksort(std::vector<int> &list, int left, int right);
   int partition(std::vector<int> &list, int left, int right);
}

QuickSort.cpp

#include "quicksort.hpp"
namespace alg = algorithms;
void alg::quicksort(std::vector<int> &list)
{
   alg::quicksort(list, 0, list.size() - 1);
}
void alg::quicksort(std::vector<int> &list, int left, int right)
{
   if (left >= right) 
      return;
   int oldPivot = alg::partition(list, left, right);
   alg::quicksort(list, left, oldPivot - 1);
   alg::quicksort(list, oldPivot + 1, right);
}
int alg::partition(std::vector<int> &list, int left, int right)
{
   int pivot = list[left + (right-left)/2];
   while (true)
   {
      while (list[left] < pivot)
         left++;
      while (list[right] > pivot)
         right--;
      if (left >= right)
         return left;
      std::swap(list[left], list[right]);
   }
}

考虑到上述背景,我有两个问题。

首先,对于单个QuickSort实现,我是否适当地使用了标头文件并以一种很好的方式构造了我的代码?例如,算法不在课堂外吗?

其次,在避免代码重复和命名冲突的同时,如何创建本算法的不同版本?例如,我应该使用类或其他语言构造吗?

我将不胜感激的是,如果答案包括最小代码段,阐明了应如何使用任何相关语言构造来实现整齐的代码。

您可以做类似地执行STD的方法。算法的执行策略。它使用标签,可以轻松地使用结构:

#include <iostream>
struct versionA{};
struct versionB{};
int fooA(versionA tag, int param)
{
    (void)tag;//Silences "unused parameter" warning
    return 2*param;
}
int fooB(versionB tag,int param)
{
    (void)tag;
    return 5*param;
}
int main()
{
    std::cout<<fooA(versionA{},5)<<'n';
    std::cout<<fooB(versionB{},5)<<'n';
//Outputs:
//10
//25
}

编译器可以优化这些空的未使用的结构,因此对此没有任何伤害。替代方法是将模板参数为标签类型,并将它们完全专门用于单个版本。但是,这种方法几乎没有缺点 - 泄漏到标头文件的实现,模板功能不能部分专业化,如果算法本身需要模板参数,则效果不佳。与超载混合的模板可能并不总是会导致调用预期功能。

如果函数中的{}呼叫您打扰您,则可以制作这些结构的全局实例并改为通过它们(通过复制)。

回答您的第一个问题:是的,您正确使用了它们。很小的注释-#pragma once不是标准的C ,但是所有标准编译器无论如何都支持它。适当的选择是使用包括警卫。

标记的完整示例:

// header file
#include <vector>
namespace algorithms 
{
namespace ver
{
    struct FixedPivot_tag{};
    struct RandomPivot_tag{};
    const extern FixedPivot_tag FixedPivot;
    const extern RandomPivot_tag RandomPivot;
}
void quicksort(ver::FixedPivot_tag tag, std::vector<int> &list, int left, int right);
void quicksort(ver::RandomPivot_tag tag, std::vector<int> &list, int left, int right);
}
// cpp file
namespace algorithms
{
    namespace ver
    {
        constexpr const FixedPivot_tag FixedPivot{};
        constexpr const RandomPivot_tag RandomPivot{};
    }
void quicksort(ver::FixedPivot_tag tag, std::vector<int> &list, int left, int right)
{
    (void)tag;
    //...
}
void quicksort(ver::RandomPivot_tag tag, std::vector<int> &list, int left, int right)
{
    (void)tag;
    //...
}
}
// usage
int main()
{
    std::vector <int> vector{5,4,3,2,1};
    using namespace algorithms;
    quicksort(ver::FixedPivot,vector,0,4);
    quicksort(ver::RandomPivot,vector,0,4);
}

如果您认为可以独立选择的参数可能存在不同的参数(例如pivotinsertion),则应考虑enum S(或更好的enum class ES)。

您可以将信息作为参数(使用标准if的运行时调度)或模板参数(使用if constexpr进行编译时调度)传递信息。代码将如下:

namespace alg {
enum class pivot { first=0; last=1; middle=2};
enum class insertion { off=0; on=1 };
template <pivot p, insertion i>
int partition(std::vector<int> &list, int left, int right)
{
    int pivot;
    if constexpr (p==pivot::first)
        pivot = list[left];
    else if constexpr (p==pivot::last)
        pivot = list[right];
    else // if constexpr (p==pivot::first)
        pivot = list[left + (right-left)/2];
    while (true)
    {
        while (list[left] < pivot)
            left++;
        while (list[right] > pivot)
            right--;
        if (left >= right)
            return left;
        std::swap(list[left], list[right]);
    }
}
template <pivot p, insertion i>
void quicksort_section( std::vector<int>& list, int begin, int end )
{
   if (left >= right) 
       return;
   int oldPivot = partition(list, left, right);
   quicksort_section(list, left, oldPivot - 1);
   quicksort_section(list, oldPivot + 1, right);
}
template <pivot p, insertion i>
void quicksort(std::vector<int> &list)
{
    quicksort_section<p,i>(list, 0, list.size() - 1);
}
} // namespace alg

请注意,模板通常在标题文件中实现。

这解决了您的所有需求。它是可扩展的,并且避免了代码重复,即,您不必单独处理所有n_pivot * n_insertion可能性。

如果您使用旧的C 版本(PRE C 17),则可以删除constexpr(使用一些宏观技巧),因为任何合理的编译器都会消除发生的死亡代码。