This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
I have this code and I am suppose to re implement it using a quadratic probing method, the algorithm I have is
i = (i + count) % CAPACITY;. I'm not sure how I am supposed to do this, so help would be nice. I'm thinking you would just change the hash function and the next_index function, I'm not too sure. Here is the code I need to re-implement. Header file on top template on bottom.
#ifndef TABLE1_H
#define TABLE1_H
#include <cstdlib> // Provides size_t
namespace main_savitch_12A
{
template <class RecordType>
class table
{
public:
// MEMBER CONSTANT -- See Appendix E if this fails to compile.
static const std::size_t CAPACITY = 811;
// CONSTRUCTOR
table( );
// MODIFICATION MEMBER FUNCTIONS
void insert(const RecordType& entry);
void remove(int key);
// CONSTANT MEMBER FUNCTIONS
bool is_present(int key) const;
void find(int key, bool& found, RecordType& result) const;
std::size_t size( ) const { return used; }
private:
// MEMBER CONSTANTS -- These are used in the key field of special records.
static const int NEVER_USED = -1;
static const int PREVIOUSLY_USED = -2;
// MEMBER VARIABLES
RecordType data[CAPACITY];
std::size_t used;
// HELPER FUNCTIONS
std::size_t hash(int key) const;
std::size_t next_index(std::size_t index) const;
void find_index(int key, bool& found, std::size_t& index) const;
bool never_used(std::size_t index) const;
bool is_vacant(std::size_t index) const;
};
}
#include "table1.template" // Include the implementation.
#endif
//End Of Header
#include <cassert> // Provides assert
#include <cstdlib> // Provides size_t
namespace main_savitch_12A
{
template <class RecordType>
const std::size_t table<RecordType>::CAPACITY;
template <class RecordType>
const int table<RecordType>::NEVER_USED;
template <class RecordType>
const int table<RecordType>::PREVIOUSLY_USED;
template <class RecordType>
table<RecordType>::table( )
{
std::size_t i;
used = 0;
for (i = 0; i < CAPACITY; ++i)
data[i].key = NEVER_USED; // Indicates a spot that's never been used.
}
template <class RecordType>
void table<RecordType>::insert(const RecordType& entry)
// Library facilities used: cassert
{
bool already_present; // True if entry.key is already in the table
std::size_t index; // data[index] is location for the new entry
assert(entry.key >= 0);
// Set index so that data[index] is the spot to place the new entry.
find_index(entry.key, already_present, index);
// If the key wasn't already there, then find the location for the new entry.
if (!already_present)
{
assert(size( ) < CAPACITY);
index = hash(entry.key);
while (!is_vacant(index))
index = next_index(index);
++used;
}
data[index] = entry;
}
template <class RecordType>
void table<RecordType>::remove(int key)
// Library facilities used: cassert
{
bool found; // True if key occurs somewhere in the table
std::size_t index; // Spot where data[index].key == key
assert(key >= 0);
find_index(key, found, index);
if (found)
{ // The key was found, so remove this record and reduce used by 1.
data[index].key = PREVIOUSLY_USED; // Indicates a spot that's no longer in use.
--used;
}
}
template <class RecordType>
bool table<RecordType>::is_present(int key) const
// Library facilities used: assert.h
{
bool found;
std::size_t index;
assert(key >= 0);
find_index(key, found, index);
return found;
}
template <class RecordType>
void table<RecordType>::find(int key, bool& found, RecordType& result) const
// Library facilities used: cassert.h
{
std::size_t index;
assert(key >= 0);
find_index(key, found, index);
if (found)
result = data[index];
}
template <class RecordType>
inline std::size_t table<RecordType>::hash(int key) const
{
return (key % CAPACITY);
}
template <class RecordType>
inline std::size_t table<RecordType>::next_index(std::size_t index) const
// Library facilities used: cstdlib
{
return ((index+1) % CAPACITY);
}
template <class RecordType>
void table<RecordType>::find_index(int key, bool& found, std::size_t& i) const
// Library facilities used: cstdlib
{
std::size_t count; // Number of entries that have been examined
count = 0;
i = hash(key);
while((count < CAPACITY) && (data[i].key != NEVER_USED) && (data[i].key != key))
{
++count;
i = next_index(i);
}
found = (data[i].key == key);
}
template <class RecordType>
inline bool table<RecordType>::never_used(std::size_t index) const
{
return (data[index].key == NEVER_USED);
}
template <class RecordType>
inline bool table<RecordType>::is_vacant(std::size_t index) const
{
return (data[index].key == NEVER_USED) || (data[index].key == PREVIOUSLY_USED);
}
}
With this code you are doing linear probing
index = hash(entry.key);
while (!is_vacant(index))
index = next_index(index);
template <class RecordType>
inline std::size_t table<RecordType>::next_index(std::size_t index) const
// Library facilities used: cstdlib
{
return ((index+1) % CAPACITY);
}
Say your map is nearly full and hash returns 23, then the next slots you are going to test will be 24, 25, 26, 27, etc.
All that is different about quadratic probing is the pattern of slots to test when a slot is full. Again suppose hash returns 23, then the next slot to test will be 23 + 1 = 24, the next will be 23 + 4 = 27, the next will be 23 + 9 = 32, the next will be 23 + 16 = 39. See the pattern? Each time you are testing 23 + n*n. That is quadratic probing. Of course all values should be mod CAPACITY, like you are doing now.
In other words you don't need to change the hash function, just the while loop inside insert.
Related
I'm looking for an implementation of a stack allocated 2d array (array of arrays) which supports O(1) reads. You can guess what I mean from the below picture. Black are filled entries, white are possible gaps from erasure.
The implementation should allow fast random access O(1) on each element but also allow insertion and erase operations which do not shift around too many elements (there might be string objects located there).
Information for each array is held in an object like this:
struct array
{
iterator begin_;
iterator end_;
array* next_;
array* prev_;
};
It contains information on where this particular array starts and the memory neighbours (prev_ and next_).
I'm looking for a proven battle hardened algorithm for insertion and erasing that I can rely on. I tried constructing a few on my own but they tend to become very complicated very quickly.
Hurdles:
When arrays are shifted, each updated array needs to somehow receive the memo (adapt begin and end pointers).
Array objects will be themselves located in an array. This means that with every additional data member of struct array, the memory requirements of the whole thing will grow by member_size * 2d_array_size.
I'm open for all suggestions!
I am thinking of an idea, where we can segment the storage into different segments of size n. entire buffer size will be the multiple of n.
When a new array is to be initialized, we allocate a segment to it. normal array operations can be performed there. when it needs more space, it request for one more segment, and if more segment space available we allocate it to them to extend it.
In this case, Minimum length of an array cannot go below segment size n. this size n can be fine tuned as per requirement for better space efficiency and utilization.
Each segment is numbered, so we can calculate the index of an element and fetch it in O(1).
Sample program (In Python):
class segment:
def __init__(self,number):
self.number=number
class storage:
def __init__(self):
self.size=100
self.default_value=None
self.array=[self.default_value]*self.size
self.segment_size=5
self.number_of_segments =len(self.array)//self.segment_size
self.segment_map=[None]*self.number_of_segments
def store(self,index,value):
if index<self.size and index>=0:
self.array[index]=value
def get(self,index):
return self.array[index]
def get_next_segment(self):
new_seg=None
for i,seg in enumerate(self.segment_map):
if seg == self.default_value:
new_seg= segment(i)
break
self.occupy_segment(new_seg)
self.clean_segment(new_seg)
return new_seg
def occupy_segment(self,seg):
self.segment_map[seg.number]=True
def free_segment(self,seg):
self.segment_map[seg.number]=self.default_value
def destroy_segment(self,seg):
self.clean_segment(seg)
self.free_segment(seg)
def clean_segment(self,segment):
if segment==None:
return
segment_start_index=((segment.number) * (self.segment_size)) + 0
segment_end_index=((segment.number) * (self.segment_size)) + self.segment_size
for i in range(segment_start_index,segment_end_index):
self.store(i,self.default_value)
class array:
def __init__(self,storage):
self.storage=storage
self.length=0
self.segments=[]
self.add_new_segment()
def add_new_segment(self):
new_segment=self.storage.get_next_segment()
if new_segment==None:
raise Exception("Out of storage")
self.segments.append(new_segment)
def is_full(self):
return self.length!=0 and self.length%self.storage.segment_size==0
def calculate_storage_index_of(self,index):
segment_number=index//self.storage.segment_size
element_position=index%self.storage.segment_size
return self.segments[segment_number].number * self.storage.segment_size + element_position
def add(self,value):
if self.is_full():
self.add_new_segment()
last_segement=self.segments[-1]
element_position=0
if self.length!=0:
element_position=(self.length%self.storage.segment_size)
index=(last_segement.number*self.storage.segment_size)+element_position
self.__store(index,value)
self.length+=1
def __store(self,index,value):
self.storage.store(index,value)
def update(self,index,value):
self.__store(
self.calculate_storage_index_of(index),
value
)
def get(self,index):
return self.storage.get(self.calculate_storage_index_of(index))
def destroy(self):
for seg in self.segments:
self.storage.destroy_segment(seg)
st=storage()
array1=array(st)
array1.add(3)
Hi I did not have enough time to implement a full solution (and no time to complete it) but here is the direction I was taking (live demo : https://onlinegdb.com/sp7spV_Ui)
#include <cassert>
#include <array>
#include <stdexcept>
#include <vector>
#include <iostream>
namespace meta_array
{
template<typename type_t, std::size_t N> class meta_array_t;
// template internal class not for public use.
namespace details
{
// a block contains the meta information on a subarray within the meta_array
template<typename type_t, std::size_t N>
class meta_array_block_t
{
public:
// the iterator within a block is of the same type as that of the containing array
using iterator_t = typename std::array<type_t, N>::iterator;
/// <summary>
///
/// </summary>
/// <param name="parent">parent, this link is needed if blocks need to move within the parent array</param>
/// <param name="begin">begin iterator of the block</param>
/// <param name="end">end iterator of the block (one past last)</param>
/// <param name="size">cached size (to not have to calculate it from iterator differences)</param>
meta_array_block_t(meta_array_t<type_t, N>& parent, const iterator_t& begin, const iterator_t& end, std::size_t size) :
m_parent{ parent },
m_begin{ begin },
m_end{ end },
m_size{ size }
{
}
// the begin and end methods allow a block to be used in a range based for loop
iterator_t begin() const noexcept
{
return m_begin;
}
iterator_t end() const noexcept
{
return m_end;
}
// operation to shrink the size of the last free block in the meta-array
void move_begin(std::size_t n) noexcept
{
assert(n <= m_size);
m_size -= n;
m_begin += n;
}
// operation to move a block n items back in the meta array
void move_to_back(std::size_t n) noexcept
{
m_begin += n;
m_end += n;
}
std::size_t size() const noexcept
{
return m_size;
}
// assign a new array to the sub array
// if the new array is bigger then the array that is already there
// then move the blocks after it toward the end of the meta-array
template<std::size_t M>
meta_array_block_t& operator=(const type_t(&values)[M])
{
// move all other sub-arrays back if the new sub-array is bigger
// if it is smaller then adjusting the end iterator of the block is fine
if (M > m_size)
{
m_parent.move_back(m_end, M - m_size);
}
m_size = M;
// copy will do the right thing (copy from back to front) if needed
std::copy(std::begin(values), std::end(values), m_begin);
m_end = m_begin + m_size;
return *this;
}
private:
meta_array_t<type_t, N>& m_parent;
std::size_t m_index;
iterator_t m_begin;
iterator_t m_end;
std::size_t m_size;
};
} // details
//---------------------------------------------------------------------------------------------------------------------
//
template<typename type_t, std::size_t N>
class meta_array_t final
{
public:
meta_array_t() :
m_free_size{ N },
m_size{ 0ul },
m_last_free_block{ *this, m_buffer.begin(), m_buffer.end(), N }
{
}
~meta_array_t() = default;
// meta_array is non copyable & non moveable
meta_array_t(const meta_array_t&) = delete;
meta_array_t operator=(const meta_array_t&) = delete;
meta_array_t(meta_array_t&&) = delete;
meta_array_t operator=(meta_array_t&&) = delete;
// return the number of subarrays
std::size_t array_count() const noexcept
{
return m_size;
}
// return number of items that can still be allocated
std::size_t free_size() const noexcept
{
return m_free_size;
}
template<std::size_t M>
std::size_t push_back(const type_t(&values)[M])
{
auto block = allocate(M);
std::copy(std::begin(values), std::end(values), block.begin());
return m_blocks.size();
}
auto begin()
{
return m_blocks.begin();
}
auto end()
{
return m_blocks.end();
}
auto& operator[](const std::size_t index)
{
assert(index < m_size);
return m_blocks[index];
}
private:
friend class details::meta_array_block_t<type_t, N>;
void move_back(std::array<type_t,N>::iterator begin, std::size_t offset)
{
std::copy(begin, m_buffer.end() - offset - 1, begin + offset);
// update block administation
for (auto& block : m_blocks)
{
if (block.begin() >= begin )
{
block.move_to_back(offset);
}
}
}
auto allocate(std::size_t size)
{
if ((size == 0ul) || (size > m_free_size)) throw std::bad_alloc();
if (m_last_free_block.size() < size)
{
compact();
}
m_blocks.push_back({ *this, m_last_free_block.begin(), m_last_free_block.begin() + size, size });
m_last_free_block.move_begin(size);
m_free_size -= size;
m_size++;
return m_blocks.back();
}
void compact()
{
assert(false); // not implemented yet
// todo when a gap is found between 2 sub-arrays (compare begin/end iterators) then move
// the next array to the front
// the array after that will move to the front by the sum of the gaps ... etc...
}
std::array<type_t, N> m_buffer;
std::vector<details::meta_array_block_t<type_t,N>> m_blocks;
details::meta_array_block_t<type_t,N> m_last_free_block;
std::size_t m_size;
std::size_t m_free_size;
};
} // meta_array
//---------------------------------------------------------------------------------------------------------------------
#define ASSERT_TRUE(x) assert(x);
#define ASSERT_FALSE(x) assert(!x);
#define ASSERT_EQ(x,y) assert(x==y);
static constexpr std::size_t test_buffer_size = 16;
template<typename type_t, std::size_t N>
void show_arrays(meta_array::meta_array_t<type_t, N>& meta_array)
{
std::cout << "\n--- meta_array ---\n";
for (const auto& sub_array : meta_array)
{
std::cout << "sub array = ";
auto comma = false;
for (const auto& value : sub_array)
{
if (comma) std::cout << ", ";
std::cout << value;
comma = true;
}
std::cout << "\n";
}
}
void test_construction()
{
meta_array::meta_array_t<int, test_buffer_size> meta_array;
ASSERT_EQ(meta_array.array_count(),0ul);
ASSERT_EQ(meta_array.free_size(),test_buffer_size);
}
void test_push_back_success()
{
meta_array::meta_array_t<int, test_buffer_size> meta_array;
meta_array.push_back({ 1,2,3 });
meta_array.push_back({ 14,15 });
meta_array.push_back({ 26,27,28,29 });
ASSERT_EQ(meta_array.array_count(),3ul); // cont
ASSERT_EQ(meta_array.free_size(),(test_buffer_size-9ul));
}
void test_range_based_for()
{
meta_array::meta_array_t<int, test_buffer_size> meta_array;
meta_array.push_back({ 1,2,3 });
meta_array.push_back({ 14,15 });
meta_array.push_back({ 26,27,28,29 });
show_arrays(meta_array);
}
void test_assignment()
{
meta_array::meta_array_t<int, test_buffer_size> meta_array;
meta_array.push_back({ 1,2,3 });
meta_array.push_back({ 4,5,6 });
meta_array.push_back({ 7,8,9 });
meta_array[0] = { 11,12 }; // replace with a smaller array then what there was
meta_array[1] = { 21,22,23,24 }; // replace with a bigger array then there was
show_arrays(meta_array);
}
//---------------------------------------------------------------------------------------------------------------------
int main()
{
test_construction();
test_push_back_success();
test_range_based_for();
test_assignment();
return 0;
}
I have to write an ArrayDictionary.cpp file that follows the ArrayDictionary.h file listed below. The purpose of the .h file is an array-based implementation of the ADT dictionary that organizes its data items in sorted search-key order. Search keys in the dictionary are unique.
The code includes ArrayDictionary.h and ArrayDictionary.cpp. The ArrayDictionary.h file also includes other .h files. Please let me know if you need them to help me.
I'm having trouble with variables that cannot be identify in the ArrayDicionary.cpp file. The variables are items and entryCount that are having errors.
//ArrayDictionary.h
#pragma once
#ifndef _ARRAY_DICTIONARY
#define _ARRAY_DICTIONARY
#include "DictionaryInterface.h"
#include "Entry.h"
#include "NotFoundException.h"
#include "PrecondViolatedExcept.h"
template < class KeyType, class ValueType>
class ArrayDictionary : public DictionaryInterface < KeyType, ValueType>
{
private:
static const int DEFAULT_CAPACITY = 21; // Small capacity to test for
// a full dictionary
Entry<KeyType, ValueType>* entries; // Array of dictionary entries
int entryCount; // Maximum capacity of the dictionary
void destroyDictionary();
int findEntryIndex(int firstIndex, int lastIndex, const KeyType & searchKey) const;
int maxEntries;
public:
ArrayDictionary();
ArrayDictionary(int maxNumberOfEntries);
ArrayDictionary(const ArrayDictionary<KeyType, ValueType>&dictionary);
virtual ~ArrayDictionary();
bool isEmpty() const;
int getNumberOfEntries() const;
bool add(const KeyType& searchKey, const ValueType& newValue) throw (PrecondViolatedExcep);
bool remove(const KeyType& searchKey);
void clear();
ValueType getValue(const KeyType& searchKey) const throw (NotFoundException);
bool contains(const KeyType& searchKey) const;
/** Traverses the items in this dictionary in sorted search-key order
and calls a given client function once for each item. */
void traverse(void visit(ValueType&)) const;
}; // end ArrayDictionary
#include "ArrayDictionary.cpp"
#endif
#include <iostream>
#include "ArrayDictionary.h"
#include "PrecondViolatedExcept.h"
template < class KeyType, class ValueType>
void ArrayDictionary<KeyType, ValueType>::destroyDictionary()
{
delete[] items;
items = new Entry[maxEntries];
entryCount = 0;
}
template < class KeyType, class ValueType>
inline int findEntryIndex(int firstIndex, int lastIndex, const KeyType& searchKey)
{
int IndexMiddle = firstIndex + (lastIndex - firstIndex) / 2;
if (firstIndex > lastIndex)
return -1;
else if (searchKey == items[IndexMiddle].getKey())
return IndexMiddle;
else if (searchKey < items[IndexMiddle].getKey())
return findEntryIndex(firstIndex, IndexMiddle - 1, searchKey);
else
return findEntryIndex(IndexMiddle + 1, lastIndex, searchKey);
}
template < class KeyType, class ValueType>
inline ArrayDictionary<KeyType, ValueType>::ArrayDictionary() : entryCount(0), maxEntries(DEFAULT_CAPACITY)
{
items = new Entry[DEFAULT_CAPACITY];
}
template < class KeyType, class ValueType>
inline ArrayDictionary<KeyType,ValueType>::ArrayDictionary(int maxNumberOfEntries) :
entryCount(0), maxEntries(maxNumberOfEntries)
{
items = new Entry[maxNumberOfEntries];
}
template < class KeyType, class ValueType>
inline ArrayDictionary<KeyType,ValueType>::ArrayDictionary(const ArrayDictionary& dictionary) :
entryCount(dictionary.itemCount), maxEntries(dictionary.maxEntries)
{
items = new Entry[dictionary.maxEntries];
for (int index = 0; index < dictionary.entryCount; index++)
{
items[index] = dictionary.items[index];
}
}
template < class KeyType, class ValueType>
inline ArrayDictionary<KeyType, ValueType>::~ArrayDictionary()
{
destroyDictionary();
}
template < class KeyType, class ValueType>
inline bool isEmpty()
{
return (entryCount == 0);
}
template < class KeyType, class ValueType>
inline int getNumberOfItems()
{
return entryCount;
}
template < class KeyType, class ValueType>
inline void ArrayDictionary<KeyType, ValueType>:: clear()
{
destroyDictionary();
}
template < class KeyType, class ValueType>
inline bool ArrayDictionary<KeyType, ValueType>::add(const KeyType& searchKey, const ValueType& newValue) throw (PrecondViolatedExcep)
{
bool ableToInsert = (entryCount < maxEntries);
if (ableToInsert)
{
// Make room for new entry by shifting all entries at
// positions >= newPosition toward the end of the array
// (no shift if newPosition == itemCount + 1). Performing
// a binary search doesn’t help here, because we need to
// shift the entries while looking for the insertion location.
int index = entryCount;
// Short-circuit evaluation is important
while ((index > 0) && (searchKey < items[index - 1].getKey()))
{
items[index] = items[index - 1];
index--;
} // end while
if (searchKey != items[index - 1].getKey())
{
items[index] = Entry<KeyType, ValueType>(searchKey, newValue);
entryCount++;
}
else
{
auto message = "Attempt to add entry whose search key exits in dictionary.";
throw (PrecondViolatedExcep(message);
}
return ableToInsert;
} // end add
}
template < class KeyType, class ValueType>
inline bool ArrayDictionary<KeyType, ValueType>:: remove(const const KeyType& itemKey)
{
int currentIndex = findEntryIndex(0, itemCount - 1, itemKey);
bool deletable = !isEmpty() && (currentIndex >= 0);
if (deletable)
{
while (currentIndex < entryCount - 1)
{
items[currentIndex] = items[currentIndex + 1];
currentIndex++;
}
itemCount--;
}
return deletable;
}
template < class KeyType, class ValueType>
inline ValueType getValue(const KeyType& searchKey) throw(NotFoundException)
{
int currentIndex = findEntryIndex(0, itemCount - 1, searchKey);
if (currentIndex < 0)
throw NotFoundException("nnItemis not in the Dictionary!nn");
return items[currentIndex].getItem();
}
template < class KeyType, class ValueType>
inline bool contains(const KeyType& searchKey)
{
return (findEntryIndex(0, entryCount - 1, itemKey) >= 0)
}
template < class KeyType, class ValueType>
inline void traverse(void visit(ValueType&))
{
for (int itr = 0; itr < entryCount; itr++)
{
ValueType currentItem = items[itr].getItem();
visit(currentItem);
}
}
I am trying to make class that acts as multidimensional vector. It doesn't have to do anything fancy. I basically want to have a "container" class foo where I can access elements by foo[x][y][z]. Now I would also need similar classes for foo[x][y] and foo[x]. Which lead me to ponder about the following (more general) question, is there a way to make something like this where you can just initialize as foo A(a,b,c,...) for any n number of arguments and get a n-dimensional vector with elements accessible by [][][]...? Below the class I have for (in example) the four-dimensional case.
First the header
#ifndef FCONTAINER_H
#define FCONTAINER_H
#include <iostream>
using namespace std;
class Fcontainer
{
private:
unsigned dim1, dim2, dim3, dim4 ;
double* data;
public:
Fcontainer(unsigned const dims1, unsigned const dims2, unsigned const dims3, unsigned const dims4);
~Fcontainer();
Fcontainer(const Fcontainer& m);
Fcontainer& operator= (const Fcontainer& m);
double& operator() (unsigned const dim1, unsigned const dim2, unsigned const dim3, unsigned const dim4);
double const& operator() (unsigned const dim1, unsigned const dim2, unsigned const dim3, unsigned const dim4) const;
};
#endif // FCONTAINER_H
Now the cpp:
#include "fcontainer.hpp"
Fcontainer::Fcontainer(unsigned const dims1, unsigned const dims2, unsigned const dims3, unsigned const dims4)
{
dim1 = dims1; dim2 = dims2; dim3 = dims3; dim4 = dims4;
if (dims1 == 0 || dims2 == 0 || dims3 == 0 || dims4 == 0)
throw std::invalid_argument("Container constructor has 0 size");
data = new double[dims1 * dims2 * dims3 * dims4];
}
Fcontainer::~Fcontainer()
{
delete[] data;
}
double& Fcontainer::operator() (unsigned const dims1, unsigned const dims2, unsigned const dims3, unsigned const dims4)
{
if (dims1 >= dim1 || dims2 >= dim2 || dims3 >= dim3 || dims4 >= dim4)
throw std::invalid_argument("Container subscript out of bounds");
return data[dims1*dim2*dims3*dim4 + dims2*dim3*dim4 + dim3*dim4 + dims4];
}
double const& Fcontainer::operator() (unsigned const dims1, unsigned const dims2, unsigned const dims3, unsigned const dims4) const
{
if(dims1 >= dim1 || dims2 >= dim2 || dims3 >= dim3 || dims4 >= dim4)
throw std::invalid_argument("Container subscript out of bounds");
return data[dims1*dim2*dims3*dim4 + dims2*dim3*dim4 + dim3*dim4 + dims4];
}
So I want to expand this to an arbitrary amount of dimensions. I suppose it will take something along the lines of a variadic template or an std::initializer_list but I am not clear on how to approach this( for this problem).
Messing around in Visual Studio for a little while, I came up with this nonsense:
template<typename T>
class Matrix {
std::vector<size_t> dimensions;
std::unique_ptr<T[]> _data;
template<typename ... Dimensions>
size_t apply_dimensions(size_t dim, Dimensions&& ... dims) {
dimensions.emplace_back(dim);
return dim * apply_dimensions(std::forward<Dimensions>(dims)...);
}
size_t apply_dimensions(size_t dim) {
dimensions.emplace_back(dim);
return dim;
}
public:
Matrix(std::vector<size_t> dims) : dimensions(std::move(dims)) {
size_t size = flat_size();
_data = std::make_unique<T[]>(size);
}
template<typename ... Dimensions>
Matrix(size_t dim, Dimensions&&... dims) {
size_t size = apply_dimensions(dim, std::forward<Dimensions>(dims)...);
_data = std::make_unique<T[]>(size);
}
T & operator()(std::vector<size_t> const& indexes) {
if(indexes.size() != dimensions.size())
throw std::runtime_error("Incorrect number of parameters used to retrieve Matrix Data!");
return _data[get_flat_index(indexes)];
}
T const& operator()(std::vector<size_t> const& indexes) const {
if (indexes.size() != dimensions.size())
throw std::runtime_error("Incorrect number of parameters used to retrieve Matrix Data!");
return _data[get_flat_index(indexes)];
}
template<typename ... Indexes>
T & operator()(size_t idx, Indexes&& ... indexes) {
if (sizeof...(indexes)+1 != dimensions.size())
throw std::runtime_error("Incorrect number of parameters used to retrieve Matrix Data!");
size_t flat_index = get_flat_index(0, idx, std::forward<Indexes>(indexes)...);
return at(flat_index);
}
template<typename ... Indexes>
T const& operator()(size_t idx, Indexes&& ... indexes) const {
if (sizeof...(indexes)+1 != dimensions.size())
throw std::runtime_error("Incorrect number of parameters used to retrieve Matrix Data!");
size_t flat_index = get_flat_index(0, idx, std::forward<Indexes>(indexes)...);
return at(flat_index);
}
T & at(size_t flat_index) {
return _data[flat_index];
}
T const& at(size_t flat_index) const {
return _data[flat_index];
}
size_t dimension_size(size_t dim) const {
return dimensions[dim];
}
size_t num_of_dimensions() const {
return dimensions.size();
}
size_t flat_size() const {
size_t size = 1;
for (size_t dim : dimensions)
size *= dim;
return size;
}
private:
size_t get_flat_index(std::vector<size_t> const& indexes) const {
size_t dim = 0;
size_t flat_index = 0;
for (size_t index : indexes) {
flat_index += get_offset(index, dim++);
}
return flat_index;
}
template<typename ... Indexes>
size_t get_flat_index(size_t dim, size_t index, Indexes&& ... indexes) const {
return get_offset(index, dim) + get_flat_index(dim + 1, std::forward<Indexes>(indexes)...);
}
size_t get_flat_index(size_t dim, size_t index) const {
return get_offset(index, dim);
}
size_t get_offset(size_t index, size_t dim) const {
if (index >= dimensions[dim])
throw std::runtime_error("Index out of Bounds");
for (size_t i = dim + 1; i < dimensions.size(); i++) {
index *= dimensions[i];
}
return index;
}
};
Let's talk about what this code accomplishes.
//private:
template<typename ... Dimensions>
size_t apply_dimensions(size_t dim, Dimensions&& ... dims) {
dimensions.emplace_back(dim);
return dim * apply_dimensions(std::forward<Dimensions>(dims)...);
}
size_t apply_dimensions(size_t dim) {
dimensions.emplace_back(dim);
return dim;
}
public:
Matrix(std::vector<size_t> dims) : dimensions(std::move(dims)) {
size_t size = flat_size();
_data = std::make_unique<T[]>(size);
}
template<typename ... Dimensions>
Matrix(size_t dim, Dimensions&&... dims) {
size_t size = apply_dimensions(dim, std::forward<Dimensions>(dims)...);
_data = std::make_unique<T[]>(size);
}
What this code enables us to do is write an initializer for this matrix that takes an arbitrary number of dimensions.
int main() {
Matrix<int> mat{2, 2}; //Yields a 2x2 2D Rectangular Matrix
mat = Matrix<int>{4, 6, 5};//mat is now a 4x6x5 3D Rectangular Matrix
mat = Matrix<int>{9};//mat is now a 9-length 1D array.
mat = Matrix<int>{2, 3, 4, 5, 6, 7, 8, 9};//Why would you do this? (yet it compiles...)
}
And if the number and sizes of the dimensions is only known at runtime, this code will work around that:
int main() {
std::cout << "Input the sizes of each of the dimensions.\n";
std::string line;
std::getline(std::cin, line);
std::stringstream ss(line);
size_t dim;
std::vector<size_t> dimensions;
while(ss >> dim)
dimensions.emplace_back(dim);
Matrix<int> mat{dimensions};//Voila.
}
Then, we want to be able to access arbitrary indexes of this matrix. This code offers two ways to do so: either statically using templates, or variably at runtime.
//public:
T & operator()(std::vector<size_t> const& indexes) {
if(indexes.size() != dimensions.size())
throw std::runtime_error("Incorrect number of parameters used to retrieve Matrix Data!");
return _data[get_flat_index(indexes)];
}
T const& operator()(std::vector<size_t> const& indexes) const {
if (indexes.size() != dimensions.size())
throw std::runtime_error("Incorrect number of parameters used to retrieve Matrix Data!");
return _data[get_flat_index(indexes)];
}
template<typename ... Indexes>
T & operator()(size_t idx, Indexes&& ... indexes) {
if (sizeof...(indexes)+1 != dimensions.size())
throw std::runtime_error("Incorrect number of parameters used to retrieve Matrix Data!");
size_t flat_index = get_flat_index(0, idx, std::forward<Indexes>(indexes)...);
return at(flat_index);
}
template<typename ... Indexes>
T const& operator()(size_t idx, Indexes&& ... indexes) const {
if (sizeof...(indexes)+1 != dimensions.size())
throw std::runtime_error("Incorrect number of parameters used to retrieve Matrix Data!");
size_t flat_index = get_flat_index(0, idx, std::forward<Indexes>(indexes)...);
return at(flat_index);
}
And then, in practice:
Matrix<int> mat{6, 5};
mat(5, 2) = 17;
//mat(5, 1, 7) = 24; //throws exception at runtime because of wrong number of dimensions.
mat = Matrix<int>{9, 2, 8};
mat(5, 1, 7) = 24;
//mat(5, 2) = 17; //throws exception at runtime because of wrong number of dimensions.
And this works fine with runtime-dynamic indexing:
std::vector<size_t> indexes;
/*...*/
mat(indexes) = 54; //Will throw if index count is wrong, will succeed otherwise
There are a number of other functions that this kind of object might want, like a resize method, but choosing how to implement that is a high-level design decision. I've also left out tons of other potentially valuable implementation details (like an optimizing move-constructor, a comparison operator, a copy constructor) but this should give you a pretty good idea of how to start.
EDIT:
If you want to avoid use of templates entirely, you can cut like half of the code provided here, and just use the methods/constructor that uses std::vector<size_t> to provide dimensions/index data. If you don't need the ability to dynamically adapt at runtime to the number of dimensions, you can remove the std::vector<size_t> overloads, and possibly even make the number of dimensions a template argument for the class itself (which would enable you to use size_t[] or std::array[size_t, N] to store dimensional data).
Well, assuming you care about efficiency at all, you probably want to store all of the elements in a contiguous manner regardless. So you probably want to do something like:
template <std::size_t N, class T>
class MultiArray {
MultiArray(const std::array<std::size_t, N> sizes)
: m_sizes(sizes)
, m_data.resize(product(m_sizes)) {}
std::array<std::size_t, N> m_sizes;
std::vector<T> m_data;
};
The indexing part is where it gets kind of fun. Basically, if you want a[1][2][3] etc to work, you have to have a return some kind of proxy object, that has its own operator[]. Each one would have to be aware of its own rank. Each time you do [] it returns a proxy letting you specify the next index.
template <std::size_t N, class T>
class MultiArray {
// as before
template <std::size_t rank>
class Indexor {
Indexor(MultiArray& parent, const std::array<std::size_t, N>& indices = {})
: m_parent(parent), m_indices(indices) {}
auto operator[](std::size_t index) {
m_indices[rank] = index;
return Indexor<rank+1>(m_indices, m_parent);
}
std::array<std::size_t, N> m_indices;
MultiArray& m_parent;
};
auto operator[](std::size_t index) {
return Indexor<0>(*this)[index];
}
}
Finally, you have a specialization for when you're done with the last index:
template <>
class Indexor<N-1> { // with obvious constructor
auto operator[](std::size_t index) {
m_indices[N-1] = index;
return m_parent.m_data[indexed_product(m_indices, m_parent.m_sizes)];
}
std::array<std::size_t, N> m_indices;
MultiArray& m_parent;
};
Obviously this is a sketch but at this point its just filling out details and getting it to compile. There are other approaches like instead having the indexor object have two iterators and narrowing but that seemed a bit more complex. You also don't need to template the Indexor class and could use a runtime integer instead but that would make it very easy to misuse, having one too many or too few [] would be a runtime error, not compile time.
Edit: you would also be able to initialize this in the way you describe in 17, but not in 14. But in 14 you can just use a function:
template <class ... Ts>
auto make_double_array(Ts ts) {
return MultiArray<sizeof ... Ts, double>(ts...);
}
Edit2: I use product and indexed_product in the implementation. The first is obvious, the second is less so, but hopefully they should be clear. The latter is a function that given an array of dimensions, and an array of indices, would return the position of that element in the array.
I'm trying to reverse a string using stacks. It correctly reverses the string, but the for loop crashes when i reaches 0. I get a "string subscript out of range" error. Currently the for loop only decrements to 1. How can I get it to push and display s1[0]?
This is the main code:
#include <cstdlib> // Provides EXIT_SUCCESS
#include <iostream> // Provides cin, cout
#include <stack> // Provides stack
#include <string> // Provides string
using namespace std;
. . .
string reverse(string & s1)
{
stack<char> stk1;
string::size_type i;
// this for loop sets the rest of the characters
for (i = s1.size() - 1; i > 0; i--)
{
stk1.push(s1[i]);
cout << stk1.top();
}
return "The function was a success. Now that's what I call reverse psychology.";
}
This is the header file:
#ifndef MAIN_SAVITCH_STACK1_H
#define MAIN_SAVITCH_STACK1_H
#include <cstdlib> // Provides size_t
namespace main_savitch_7A
{
template <class Item>
class stack
{
public:
// TYPEDEFS AND MEMBER CONSTANT -- See Appendix E if this fails to compile.
typedef std::size_t size_type;
typedef Item value_type;
static const size_type CAPACITY = 30;
// CONSTRUCTOR
stack( ) { used = 0; }
// MODIFICATION MEMBER FUNCTIONS
void push(const Item& entry);
void pop( );
// CONSTANT MEMBER FUNCTIONS
bool empty( ) const { return (used == 0); }
size_type size( ) const { return used; }
Item top( ) const;
private:
Item data[CAPACITY]; // Partially filled array
size_type used; // How much of array is being used
};
}
#include "stack1.template" // Include the implementation.
#endif
And this is the stack implementation (a template file):
#include <cassert> // Provides assert
namespace main_savitch_7A
{
template <class Item>
const typename stack<Item>::size_type stack<Item>::CAPACITY;
template <class Item>
void stack<Item>::push(const Item& entry)
// Library facilities used: cassert
{
assert(size( ) < CAPACITY);
data[used] = entry;
++used;
}
template <class Item>
void stack<Item>::pop( )
// Library facilities used: cassert
{
assert(!empty( ));
--used;
}
template <class Item>
Item stack<Item>::top( ) const
// Library facilities used: cassert
{
assert(!empty( ));
return data[used-1];
}
}
I want to change the for loop to this, but it doesn't work:
// this for loop sets the rest of the characters
for (i = s1.size() - 1; i >= 0; i--) // i > -1 doesn't work either
{
stk1.push(s1[i]);
cout << stk1.top();
}
cout << s1[0] << "\n\n";
return "The function was a success. Now that's what I call reverse psychology.";
}
I can think of the following couple of options.
Using the string::size_type for the loop counter:
string::size_type i;
for (i = s1.size(); i > 0; i--)
{
stk1.push(s1[i-1]);
cout << stk1.top();
}
or
Using an int for the loop counter:
int i = 0;
for (i = s1.size()-1; i >= 0; i--)
{
stk1.push(s1[i]);
cout << stk1.top();
}
i is unsigned so it wraps around when it is decremented if it is equal to 0. You need to use a signed type for it or to check the boundary condition without involving negative numbers(that is, do not compare it with -1 and do not decrement it if it is 0).
I am trying to initialize a 2D concurrent_hash_map, a container available in the Intel TBB library. Compilation passes and there is no error at runtime. However, not all initialized values are available in the container leading to incorrect behavior.
The hash map is defined as
template<typename K>
struct MyHashCompare {
static size_t hash(const K& key) { return boost::hash_value(key); }
static bool equal(const K& key1, const K& key2) { return (key1 == key2); }
};
typedef concurrent_hash_map<int, int, MyHashCompare<int> > ColMap;
typedef concurrent_hash_map<int, ColMap, MyHashCompare<int> > RowMap;
The function object is defined as follows. Could the reason for the incorrect behavior originate here?
class ColumnInit {
RowMap *const pMyEdgeMap;
public:
void operator()(const blocked_range<size_t>& r) const {
RowMap* pEdgeMap = pMyEdgeMap;
RowMap::accessor accessX;
ColMap::accessor accessY;
for(size_t n1 = r.begin(); n1 != r.end(); n1++)
{
pEdgeMap->insert(accessX, n1);
for(int n2 = 1; n2 <= 64; n2++)
{
int diff = abs((int)n1 - n2);
if ((diff == 8) || (diff == 1))
{
assert((accessX->second).insert(accessY, n2));
accessY->second = -1;
}
else
{
assert((accessX->second).insert(accessY, n2));
accessY->second = 0;
}
}
}
}
ColumnInit(RowMap* pEdgeMap): pMyEdgeMap(pEdgeMap)
{
}
};
The function object is invoked from a call to parallel_for as follows:
parallel_for(blocked_range<size_t>(1,64,16), ColumnInit((RowMap*)&mEdges), simple_partitioner());
Any suggestions or feedback would be great.
Thanks.
If you intend to create a 64x64 table, use blocked_range(1,65,16) as the first argument to parallel_for. The reason is that a blocked_range represents a half-open interval, which includes the lower bound but excludes the upper bound.