Spring 2006 CS 188

Homework 1 Solution

  1. They all do the same thing: they replace each empty string in a with "EMPTY". (Stylistically, I'd choose the replace_if version, because the name of the algorithm communicates most specifically what we're doing: replacing values if some predicate holds.)

    1. double distance_from_origin(double p[], int n)  // Assume p has n elements
      {
          return sqrt(inner_product(p, p+n, p, 0.0));
      }
      
    2. struct DiffSquared
      {
          double operator()(double x, double y) const
          { return (x-y)*(x-y); }
      };
      
      double dist(double p[], double q[], int n)  // Assume p and q have n elements
      {
          return sqrt(inner_product(p, p+n, q, 0.0, plus<double>(), DiffSquared()));
      }
      
    3. template<typename InIt1, typename InIt2>
      int count_matches(InIt1 begin1, InIt1 end1, InIt2 begin2)
      {
         int n = 0;
         for ( ; begin1 != end1; begin1++, begin2++)
         {
            if (*begin1 == *begin2)
               n++;
         }
         return n;
      }
      

      Here's a solution that has no loops, but uses something we hadn't talked about:

      #include <numeric>    // for inner_product
      #include <iterator>   // for iterator_traits
      #include <functional> // for plus
      using namespace std;
      
      template<typename T1, typename T2>
      struct Equals
      {
         bool operator()(const T1& lhs, const T2& rhs) const
         { return lhs == rhs; }
      };
      
      template<typename InIt1, typename InIt2>
      int count_matches(InIt1 begin1, InIt1 end1, InIt2 begin2)
      {
         return inner_product(begin1, end1, begin2, 0, plus<int>(),
                      Equals<typename iterator_traits<InIt1>::value_type, 
                             typename iterator_traits<InIt2>::value_type>());
      }
      
    4. template<typename InIt1, typename InIt2, typename Pred>
      int count_matches(InIt1 begin1, InIt1 end1, InIt2 begin2, Pred p)
      {
         return inner_product(begin1, end1, begin2, 0, plus<int>(), p);
      }
      
  2. #include <iostream>
    #include <iomanip>
    #include <string>
    #include <vector>
    #include <algorithm>
    #include <cstdlib>  // for rand
    using namespace std;
    
    const size_t TOP_N = 10;
    
    struct Gag
    {
       Gag(string t, double f) : text(t), funniness(f) {}
       string text;
       double funniness;
    };
    
    bool is_funnier(const Gag& lhs, const Gag& rhs)
    {
       return lhs.funniness > rhs.funniness;
    }
    
    char random_letter()
    {
       return 'a' + rand() % 26;
    }
    
    Gag make_random_gag()
    {
       string s;
       generate_n(back_inserter(s), 6, random_letter);
       return Gag(s, (100.0 * rand()) / RAND_MAX);
    }
    
    int main()
    {
       vector<Gag> v;
       generate_n(back_inserter(v), 100000, make_random_gag);
       partial_sort(v.begin(), v.begin()+TOP_N, v.end(), is_funnier);
       cout.setf(ios::fixed);
       cout.setf(ios::showpoint);
       cout.precision(4);
       for (int k = TOP_N; k >= 1; k--)
          cout << setw(2) << k << ". " << v[k-1].text << " "
               << v[k-1].funniness << endl;
    }
    
  3. #include <iostream>
    #include <sstream>
    #include <string>
    #include <vector>
    #include <algorithm>
    #include <numeric>
    #include <functional>
    #include <iterator>
    
    using namespace std;
    
    int intabs(int k)
    {
        return k < 0 ? -k : k;
    }
    
    int main()
    {
        int n;
        while (cin >> n)
        {
    	vector<int> v;
    	v.reserve(n);  // not necessary, just a minor optimization
    	string s;
    	getline(cin,s);
    	istringstream iss(s);
    	copy(istream_iterator<int>(iss), istream_iterator<int>(),
    			                           back_inserter(v));
    	adjacent_difference(v.begin(), v.end(), v.begin());
    	transform(v.begin()+1, v.end(), v.begin()+1, intabs);
    	sort(v.begin()+1, v.end());
    	if (n == 1  ||  (v[1] == 1 && v[n-1] == n-1 &&
    		adjacent_find(v.begin()+1, v.end(), greater_equal<int>())
    								== v.end()))
    	    cout << "Jolly";
    	else
    	    cout << "Not jolly";
    	cout << endl;
        }
    }
    

    Of course, while short and sweet, this solution isn't as efficient as it could be: The sort is O(N log N), but we can determine whether a sequence is jolly in linear time. Doing so without writing a loop is not particularly interesting, though, and more trouble than it's worth. Here's one way to do it: Replace the call to sort and the if statement that follows it with

    	vector<int> seen(n-1);  // initialized to zeroes
    	for_each(v.begin()+1, v.end(), RecordOccurrence(seen));
    	if (find(seen.begin(), seen.end(), 0) == seen.end())
    	    cout << "Jolly";
    	else
    	    cout << "Not jolly";
    	cout << endl;
    

    where RecordOccurrence is defined as

    	struct RecordOccurrence
    	{
    	   RecordOccurrence(vector<int>& vv) : vec(vv) {}
    	   void operator()(int k)
    	   {
    	      if (k >= 1  &&  k <= vec.size())
    	         vec[k-1] = 1;
    	   }
    	   vector<int>& vec;
    	};