admin管理员组

文章数量:1304921

I have a vector of data of n elements. I want to print n vectors of n-1 elements where each of these vectors are the original vector data minus 1 element. Basically, it's a jackknife analysis.

For a data vector containing [ 0 1 2 3 ], I want to print the following jackedData vectors:

[ 1 2 3 ]
[ 0 2 3 ]
[ 0 1 3 ]
[ 0 1 2 ]

I've come up with the following:

int Gen()
{
    static int i( 0 );
    return i++;
}

void Print( const int& i )
{
    std::cout << i << " ";
}

int _tmain(int argc, _TCHAR* argv[])
{
    std::vector< int > data;
    
    std::generate_n( std::back_inserter( data ), 6, Gen );
    std::vector< int > jackedData( data.begin() + 1, data.end() );

    std::vector< int >::const_iterator it_data( data.begin() );
    std::vector< int >::iterator it_jData( jackedData.begin() );

    int i( 0 );

    for ( 
    ; it_jData != jackedData.end()
    ; ++it_data, ++it_jData, ++i )
    {
        std::cout << i << "\t";
        std::for_each( jackedData.begin(), jackedData.end(), Print );
        std::cout << "\n";

        *it_jData = *it_data;
    }
    
    std::cout << i << "\t";
    std::for_each( jackedData.begin(), jackedData.end(), Print );
    std::cout << "\n";

    return 0;
}

The problem is, the print has to be called once outside the loop. If it's not called outside the loop, then there is an off-by-one error.

How can this be done without having to call the print once outside of the loop?

I have a vector of data of n elements. I want to print n vectors of n-1 elements where each of these vectors are the original vector data minus 1 element. Basically, it's a jackknife analysis.

For a data vector containing [ 0 1 2 3 ], I want to print the following jackedData vectors:

[ 1 2 3 ]
[ 0 2 3 ]
[ 0 1 3 ]
[ 0 1 2 ]

I've come up with the following:

int Gen()
{
    static int i( 0 );
    return i++;
}

void Print( const int& i )
{
    std::cout << i << " ";
}

int _tmain(int argc, _TCHAR* argv[])
{
    std::vector< int > data;
    
    std::generate_n( std::back_inserter( data ), 6, Gen );
    std::vector< int > jackedData( data.begin() + 1, data.end() );

    std::vector< int >::const_iterator it_data( data.begin() );
    std::vector< int >::iterator it_jData( jackedData.begin() );

    int i( 0 );

    for ( 
    ; it_jData != jackedData.end()
    ; ++it_data, ++it_jData, ++i )
    {
        std::cout << i << "\t";
        std::for_each( jackedData.begin(), jackedData.end(), Print );
        std::cout << "\n";

        *it_jData = *it_data;
    }
    
    std::cout << i << "\t";
    std::for_each( jackedData.begin(), jackedData.end(), Print );
    std::cout << "\n";

    return 0;
}

The problem is, the print has to be called once outside the loop. If it's not called outside the loop, then there is an off-by-one error.

How can this be done without having to call the print once outside of the loop?

Share Improve this question edited Feb 4 at 4:22 H.S. 12.7k2 gold badges16 silver badges34 bronze badges asked Feb 3 at 21:11 SokaekaeSokaekae 53 bronze badges
Add a comment  | 

5 Answers 5

Reset to default 3
#include <iostream>
#include <vector>

//prints the specified range from first (inclusive) to last (exclusive)
void print_range(const int *first, const int *last)
{
    for (; first < last ;) {
        std::cout << ' ' << *first++;
    }
}

int main()
{
    std::vector<int> v = {0, 1, 2, 3}; //example vector
    
    for (size_t i=0; i < v.size(); ++i) { //for each element

        std::cout << '['; //start of line

        //print the range before i [0, i]
        print_range(
            v.data(),    //index: 0 (inclusive)
            v.data() + i //index: i (exclusive)
        );
        
        //print the range after i [i+1, v.size()]
        print_range(
            v.data() + i + 1,   //index: i+1 (inclusive)
            v.data() + v.size() //index: n (exclusive)
        );

        std::cout << " ]\n"; //end of line

    }
}

Note: there are two special cases,

  • i == 0
    • first call to print_range will do nothing (first == last == 0)
  • i == v.size() - 1
    • second call to print_range will do nothing (first == last == v.size())

There is no need to test these cases separately, since the comparison is already done in print_range (first < last).

If its allowed to modify the input vector, you can do this -

Iterate over vector of size n in a loop and in every iteration:

  • Print the last n - 1 elements of vector.
  • After this, swap the first element with element at position -
    current processing vector index + 1.

Implementation:

#include <iostream>
#include <vector>
#include <algorithm>

void Print (const int& i) {
    std::cout << i << " ";
}

int main() {
    std::vector<int> v{0, 1, 2, 3};
    
    for (std::size_t x = 0; x < v.size(); ++x) {
        std::cout << "[ ";
        std::for_each (v.begin() + 1, v.end(), Print);
        std::cout << "]" << std::endl;
        std::swap (v[0], v[(x + 1) % v.size()]);
    }
    return 0;
}

Output:

[ 1 2 3 ]
[ 0 2 3 ]
[ 0 1 3 ]
[ 0 1 2 ]

While this could be done with a single function call, I like that it's a bit clearer what's happening when there are two functions.

The idea is that you need to not print a different element each time you print the vector. You need to print n times, as stated, so loop n times, excluding the element at index n each time.

My use of the fmt library is a personal convenience on this machine. Substitue whatever printing mechanism you're using.

#include <fmt/core.h>

#include <vector>

void print_excluding_one(std::vector<int> const& v, std::size_t exclude) {
  fmt::print("[ ");
  for (std::size_t idx = 0; idx < v.size(); ++idx) {
    if (idx != exclude) {
      fmt::print("{} ", v[idx]);
    }
  }
  fmt::print("]\n");
}

void do_the_thing(std::vector<int> const& v) {
  for (std::size_t idx = 0; idx < v.size(); ++idx) {
    print_excluding_one(v, idx);
  }
}

int main() {
  std::vector<int> v{0, 1, 2, 3};

  do_the_thing(v);
}

Output:

[ 1 2 3 ]
[ 0 2 3 ]
[ 0 1 3 ]
[ 0 1 2 ]

If you can use C++23, things can go as simple as this:

#include <span>
#include <array>
#include <print>
#include <ranges>
#include <format>

void my_print(std::span<int> arr){
     using namespace std::views;
     for (auto i:iota(0,size(arr)))
          std::println( "{}"
             , std::array
             { std::span(arr | take(i)
             , std::span(arr | drop(i+1)) } 
             | join );
};

views::concat would make it even simpler if available:

concat(arr | take(i), arr | drop(i+1))

That's simpler than array/span/join mombo-jumbo. In case C++23 is not an option or any of the above utilities are not available, turning std::println into a pair of loops(one for view::take, the other for views::drop) is not that difficult. Using <ranges> extremely enhances readability; Back porting code to older platforms becomes trivial due to its readability.

Using C++23 with GCC 14.2, the ranges library and the FMT library:

#include <ranges>
#include <vector>

namespace rng = std::ranges;
namespace vws = std::views;
using vws::iota, vws::drop, vws::take;

#include <fmt/ranges.h>
using fmt::println, fmt::print;

auto main() -> int {

   auto vec_orig = vws::iota(0, 4) | rng::to<std::vector>();
   println("{}\n", vec_orig);

   for (size_t n{0}; n < vec_orig.size(); ++n) {

      auto vec = {
         vec_orig | take(n) | rng::to<std::vector>(),
         vec_orig | drop(n + 1) | rng::to<std::vector>()
      };

      println("{:}", rng::join_view(vec));
   }

   return 0;
}

Live code.

本文标签: cPrint n vectors of n1 elementsStack Overflow